# Regex-Problem



## Camino (27. Okt 2012)

Hallo,

bin mal wieder mit den Regex am verzweifeln. Und zwar möchte ich bei einem Textfeld mit DocumentFilter nur die Eingabemöglichkeit für Geldbeträge erreichen. Das Dezimaltrennzeichen soll nur ein Komma sein. Der Betrag soll maximal 6 Ziffern vor und maximal 2 nach dem Komma gestattet bekommen. Das Komma soll keinmal oder einmal erlaubt werden.

Bisher hatte ich als Regex-Pattern: [0-9,]+
Dadurch konnten aber mehrere Kommas eingegeben werden und auch mehr als 2 Nachkommastellen.

Meiner Vorstellung nach müsste das etwas Ähnliches wie das sein:
[0-9]{0,6},?[0-9]{0,2}
Also zuerst 0-6 Zahlen von 0-9, dann ein oder kein Komma und dann nochmal 0-2 Zahlen von 0-9. Vielleicht kann mir ja jemand erklären, was da falsch dran ist und wie es richtig heissen müsste.


----------



## MrClave (27. Okt 2012)

Das hier sollte dein Problem lösen: 
	
	
	
	





```
"^[\\d]{,6}[,]?[\\d]{,2}$"
```

Erlaubt 6 Vor, und 2 Nachkommastellen. Und halt ein, oder gar kein Komma.
Bin kein RegEx Profi, aber genau diesen RegEx Ausdruck habe ich schon erfolgreich angewandt.


----------



## Camino (27. Okt 2012)

Danke für die Antwort. Hab das mal getestet und folgende Fehlermeldung bekommen:


```
Exception occurred during event dispatching:
java.util.regex.PatternSyntaxException: Illegal repetition near index 4
^[\d]{,6}[,]?[\d]{,2}$
    ^
```

Wenn ich die Nullen mit in die Klammern schreibe

```
"^[\\d]{0,6}[,]?[\\d]{0,2}$"
```
dann kommt zwar keine Fehlermeldung, aber ich kann auch mehr Zeichen als 6 vor dem Komma eingeben.


----------



## MrClave (27. Okt 2012)

So hier vielleicht: 
	
	
	
	





```
"(^[\\d]{0,6}[.,]?[\\d]{0,2}$)"
```

Funktioniert bei mir..


----------



## Camino (27. Okt 2012)

Hmm, seltsam. Hat bei mir keine Auswirkung. Ich kann auch mehrere Kommas eingeben, und auch mehr als 6 Ziffern vor und 2 hinter dem Komma.


----------



## Ark (27. Okt 2012)

Probier mal 
	
	
	
	





```
^[0-9]{,6}(?:,[0-9]{,2})?|(?:[0-9]{,6})?,[0-9]{,2}$
```
. Das dürfte deiner Spezifikation recht nahe kommen. Allerdings erlaubst du auch Strings wie "," oder ",0" oder "000,". Wahrscheinlicher suchst du also so was: 
	
	
	
	





```
^(?:[1-9][0-9]{,5}|0)(?:,[0-9]{1,2})?$
```

Ach, ja: Ungetestet wie immer. 

Ark


----------



## Camino (27. Okt 2012)

Ich hab das jetzt testweise mal so geschrieben

```
"(^[0-7]{0,6}[.,]?[0-2]{0,2}$)"
```
Nach meiner Logik müssten dann ja vor dem Komma nur max. 6 Ziffern von 0-7 und hinter dem Komma max. 2 Ziffern zwischen 0-2 eingegeben werden können. Ich kann aber vor dem Komma mehr als 6 Ziffern und hinter dem Komma auch mehr als 2 Ziffern (und diese auch zwischen 0-7) eingeben.
Regex ist mir echt ein Rätsel...


----------



## Camino (27. Okt 2012)

Ark hat gesagt.:


> Probier mal
> 
> 
> 
> ...



Schade, funktioniert immer noch nicht. Ich muss auch immer die Null in die geschweifte Klammer mit reinschreiben, sonst bekomme ich die oben genannte Exception.


----------



## Ark (27. Okt 2012)

@Camino: Danke für den Hinweis wegen des Syntaxfehlers. (Den größtmöglichen Exponenten wegzulassen, scheint aber erlaubt zu sein. Irgendwie inkonsistent, finde ich. Na ja, egal.) Hab's gerade mal getestet, und spontan kann ich keinen Fehler erkennen:

```
public final class RegexTest{

	public static void main(String[] args){
		Pattern p=Pattern.compile("^(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{1,2})?$");
		Matcher m = p.matcher("");
		System.out.println(m.reset("1,23").matches());
		System.out.println(m.reset("1,234").matches());
		System.out.println(m.reset("01,23").matches());
		System.out.println(m.reset("01,2").matches());
		System.out.println(m.reset("1,2").matches());
		System.out.println(m.reset("123400,").matches());
		System.out.println(m.reset("123400,0").matches());
		System.out.println(m.reset("123400,10").matches());
		System.out.println(m.reset("123400,100").matches());
		System.out.println(m.reset("987654,43").matches());
		System.out.println(m.reset("9876543,21").matches());
		System.out.println(m.reset("123,45,6").matches());
		System.out.println(m.reset(" 123,45 ").matches());
		System.out.println(m.reset(",12").matches());
		System.out.println(m.reset("12,").matches());
		System.out.println(m.reset("00,1").matches());
		System.out.println(m.reset("0,1").matches());
		System.out.println(m.reset("00,00").matches());
		System.out.println(m.reset("0,00").matches());
		System.out.println(m.reset("0,000").matches());
		System.out.println(m.reset(",").matches());
	}

}
```
Ausgabe:

```
true
false
false
false
true
false
true
true
false
true
false
false
false
false
false
false
true
false
true
false
false
```
Welcher Testfall ist denn falsch?

Ark


----------



## test1234567890 (27. Okt 2012)

Das Komma-? ist falsch - ohne komma gehen 8 ziffern

eher sowas (erlaubt führende nullen + vor nach dem komma keine ziffern versionen:
"^(([0-9]{0,6})|([0-9]{0,6},[0-9]{0,2}))$"
bzw kann man dann noch die [1-9][0-9]{0,5} version verwenden, wenn man führende nullen nicht will.

Grüße


----------



## test1234567890 (27. Okt 2012)

Sorry - Zu spät gesehen: Arks Version sieht doch gut aus...


----------



## Camino (27. Okt 2012)

Sorry, musste erst mal den Rechner wechseln und das Projekt übertragen und noch diverse Sachen erledigen.

OK, so wie ich das sehe, liegt es vielleicht nicht unbedingt an dem Regex. Das mit den Tests sieht ja wirklich ok aus. Ich hab den Regex-Pattern halt in einer Klasse (abgeleitet von DocumentFilter), was ich bei einem Textfeld (bzw. dessen Document) gesetzt habe und als Parameter die maximale Zeichenanzahl und die Pattern-Art übergeben kann. Die Pattern selber sind in der DocumentFilter-Klasse als statische Variablen festgelegt.

Ich kann ja mal die Klasse mit dem DocumentFilter hier posten, vielleicht liegt ja da der Fehler...


```
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

/**
 * Filter für Textfelder für bestimmte Zeichenlänge und Muster
 * @param maxChars maximal zulässige Zeichenlänge
 * @param pattern Muster für Texteingabe
 */
public class DocumentSizeFilter
extends DocumentFilter {
	
	// Pattern für Buchstaben, Sonderzeichen, Leerzeichen und Bindestrich
	public static final String FULLTEXT_PATTERN = "[ A-Za-z0-9-\\xC0-\\xFF]+";
	
	// Pattern für Buchstaben, Sonderzeichen, Leerzeichen und Bindestrich (A-Za-z0-9-_.@\\xC0-\\xFF)
	public static final String EMAIL_PATTERN = "[A-Za-z0-9.@!#$%&'*+-/=?^_`{|}~]+";
	
	// Pattern für Buchstaben, Sonderzeichen, Leerzeichen und Bindestrich
	public static final String TEXT_PATTERN = "[ A-Za-z-\\xC0-\\xFF]+";
	
	// Pattern nur für Buchstaben
	public static final String ONLY_TEXT_PATTERN = "[A-Za-z]+";
	
	// Pattern nur für Ziffern
	public static final String NUMBER_PATTERN = "[0-9]+";
	
	// Pattern für Dezimalzahlen mit Komma oder Punkt	
	public static final String DECIMAL_PATTERN = "[0-9.,]+";
	
	// Pattern für Geldbeträge/Währungen mit Komma	
	//public static final String CURRENCY_PATTERN = "[0-9,]+";
	//public static final String CURRENCY_PATTERN = "[0-9]+(,+)[0-9]{0,2}";
	//public static final String CURRENCY_PATTERN = "(^[\\d]{0,6}[.,]?[\\d]{0,2}$)";
	public static final String CURRENCY_PATTERN = "^(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{1,2})?$";
	
	
	
	// Pattern für Datum (Zahlen und Punkt)	
	public static final String DATE_PATTERN = "[0-9.]+";


	private final int maxCharacters;
	private final String pattern;
	    

	public DocumentSizeFilter(final int maxChars, final String pattern) {
		maxCharacters = maxChars;
		this.pattern = pattern;
	}

	
	@Override
    public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
            throws BadLocationException {
		
        if (str.matches(pattern) && (fb.getDocument().getLength() + str.length() - length) <= maxCharacters || str.isEmpty()) {
            super.replace(fb, offs, length, str, a);
        }
        
    }

}
```

Die Pattern-Variable ist das CURRENCY_PATTERN. Im Textfeld wird der Filter so gesetzt:

```
((AbstractDocument) tfFonds.getDocument()).setDocumentFilter(new DocumentSizeFilter(9, DocumentSizeFilter.CURRENCY_PATTERN));
```

Bisher hat das mit den anderen Pattern eigentlich immer ganz gut geklappt.


----------



## Attila (27. Okt 2012)

```
public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
            throws BadLocationException {
    Document doc = fb.getDocument();
    String oldString = doc.getText(0,doc.getLength());
    String newString = oldString.substring(0, offs) + str + 
       (doc.getLength() > offs + length ? oldString.substring(offs+length) : "");       
    if ( newString.matches(pattern) || newString.isEmpty() ) {
        super.replace(fb, offs, length, str, a);
    }
}
```


----------



## Ark (27. Okt 2012)

Camino hat gesagt.:


> Bisher hat das mit den anderen Pattern eigentlich immer ganz gut geklappt.


Vielleicht liegt's am ^$ drumrum? Also vielleicht nur diesen Ausdruck hier verwenden: 
	
	
	
	





```
(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{1,2})?
```
Das ist aber nur mal so geraten. :bahnhof:

Ark


----------



## test1234567890 (28. Okt 2012)

Tip: Reservierte RegEx Zeichen musst Du escapen, und dazu gehört unter anderem auch der ".", falls du wirklich den Punkt meinst! (z.B. im Date Pattern nehme ich an, das du nicht IRGENDEIN Zeichen (= ".") erlauben willst). Gleiches gilt für Deine Sonderzeichen Pattern.


----------



## Camino (28. Okt 2012)

test1234567890 hat gesagt.:


> Tip: Reservierte RegEx Zeichen musst Du escapen, und dazu gehört unter anderem auch der ".", falls du wirklich den Punkt meinst! (z.B. im Date Pattern nehme ich an, das du nicht IRGENDEIN Zeichen (= ".") erlauben willst). Gleiches gilt für Deine Sonderzeichen Pattern.



Hmm, ich hab das gerade eben nochmal getestet. Bei meinem Pattern zum Datum kann ich tatsächlich nur Zahlen und Punkte eingeben, auch ohne Escapezeichen bei dem Punkt innerhalb der eckigen Klammer.


----------



## Camino (28. Okt 2012)

Ark hat gesagt.:


> Vielleicht liegt's am ^$ drumrum? Also vielleicht nur diesen Ausdruck hier verwenden:
> 
> 
> 
> ...



Schade, leider nicht. Damit kann ich nur 9 Zahlen eingeben, aber kein Komma.

EDIT: Anscheinend hatte ich doch eher einen Fehler in meinem DocumentFilter. Wenn ich den geänderten Code von Attila übernehme, dann klappt es mit dem Pattern "(^[\\d]{0,6}[.,]?[\\d]{0,2}$)". Das einzige Problem ist, dass ich nur 8 Zahlen und nicht 9 (wie angegeben) eintippen kann. 

Es wäre noch prima, wenn Attila mir noch erklären könnte, was in meiner alten Version des DocumentFilters falsch war.


----------



## Ark (28. Okt 2012)

@Camino: Jetzt sieht dein Regex so aus, als würden auch Strings wie "", ".", ",", "00.00", ",00", "0000,", "12345678" akzeptiert werden. oO

Ark


----------



## Camino (28. Okt 2012)

Ark hat gesagt.:


> @Camino: Jetzt sieht dein Regex so aus, als würden auch Strings wie "", ".", ",", "00.00", ",00", "0000,", "12345678" akzeptiert werden. oO
> 
> Ark



Da hast du natürlich recht. ;( Na gut, den Punkt kann ich noch rausnehmen, so dass nur ein Komma akzeptiert wird. Das kommt meinem Wunschergebnis schon ziemlich nahe.

Wenn ich das von dir vorgeschlagene Pattern nehme "(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{1,2})?" (ob mit oder ohne die Zeichen ^$), dann kann ich nur 6 Zahlen aber kein Komma eingeben.

Ich war schon am überlegen, ob ich nicht lieber ein JFormattedTextField nehmen sollte. Wobei mir das mit dem Pattern und DocumentFilter eigentlich besser gefällt. Mich würde ja aber auch mal interessieren, was bei meinem DocumentFilter falsch war.


----------



## Ark (28. Okt 2012)

Camino hat gesagt.:


> Wenn ich das von dir vorgeschlagene Pattern nehme "(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{1,2})?" (ob mit oder ohne die Zeichen ^$), dann kann ich nur 6 Zahlen aber kein Komma eingeben.


Doch, du kannst danach ein Komma (",") eingeben, aber nur, wenn danach noch eine oder zwei Ziffern (0 bis 9) kommen. Wenn du willst, dass auch Zahlen ohne Ziffern nach dem Komma akzeptiert werden (z.B. "1234,"), verwende "(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{0,2})?". Oder ich verstehe das mit dem DocumentFilter nicht.

Ark


----------



## Camino (28. Okt 2012)

Ark hat gesagt.:


> Doch, du kannst danach ein Komma (",") eingeben, aber nur, wenn danach noch eine oder zwei Ziffern (0 bis 9) kommen.



Stimmt, du hast recht, aber es geht nur, wenn ich mit dem Cursor wieder zurück gehe und dann das Komma setze. Es geht nicht, wenn ich z.B. 4 Zahlen schreibe und dann ein Komma setzen möchte.



> Oder ich verstehe das mit dem DocumentFilter nicht.



Das Gefühl hab ich langsam bei mir, dass ich mich vielleicht nochmal mit der Funktionsweise des DocumentFilter näher beschäftigen sollte.


----------



## Ark (28. Okt 2012)

Camino hat gesagt.:


> Stimmt, du hast recht, aber es geht nur, wenn ich mit dem Cursor wieder zurück gehe und dann das Komma setze. Es geht nicht, wenn ich z.B. 4 Zahlen schreibe und dann ein Komma setzen möchte.


Dann wird wohl die Überprüfung quasi die ganze Zeit (d.h., gerade vor jeder Änderung) durchgeführt, und da "1234" erlaubt ist, aber "1234," nicht, kann man nach "1234" kein Komma eingeben.

Das heißt also, dass du entweder Strings wie "1234," erlauben (und eventuelle Nullen hinter dem Komma dir dazudenken bzw. im Nachhinein hinzufügen) musst, oder du sorgst dafür, dass die Korrektur/Überprüfung der Eingabe erst bei Verlassen des entsprechenden Textfelds durchgeführt wird.

Ark


----------



## Attila (28. Okt 2012)

> Es wäre noch prima, wenn Attila mir noch erklären könnte, was in meiner alten Version des DocumentFilters falsch war.




```
public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
// String str <--- enthält nur die Änderungen, nicht den ganzen String
//...
// den ganzen Text zusammenfügen:
String newString = oldString.substring(0, offs) + str + 
       (doc.getLength() > offs + length ? oldString.substring(offs+length) : "");
```


----------



## Camino (28. Okt 2012)

Na ja, mit dem Pattern "(^[\\d]{0,6}[,]?[\\d]{0,2}$)" hat es ja fast schon so funktioniert, wie ich es gerne hätte. Die Ausnahmen ",", "00,00", ",00", "0000,", "12345678" kann ich ja versuchen, nach Verlassen des Textfeldes abzufangen. Die ersten 4 Möglichkeiten ergeben 00,00 und könnten einfach nicht akzeptiert werden. Beim letzten Fall "12345678" müsste ich überlegen, wie darauf reagiert wird. Es sollte auf jeden Fall auch die Möglichkeit geben, dass eine 6- oder weniger stellige Zahl ohne Nachkommastellen eingetragen werden kann. Vielleicht kann man auch mit einem Pattern verhindern, dass die Zahl mit einer Null anfängt oder vor dem Komma gar keine Ziffer steht.


----------



## Camino (28. Okt 2012)

Ark hat gesagt.:


> Wenn du willst, dass auch Zahlen ohne Ziffern nach dem Komma akzeptiert werden (z.B. "1234,"), verwende "(?:[1-9][0-9]{0,5}|0)(?:,[0-9]{0,2})?".



Das scheint sogar noch die beste Variante zu sein. Da ist das einzige Problem wirklich das Komma ohne Nachkommastellen, was man ja einfach als ..,00 interpretieren könnte.


----------

