DocumentFilter und Regex

Hallo, hab mal wieder Probleme mit Regex. Und zwar hab ich einen DocumentFilter für ein JTextField, bei dem Geldbeträge eingegeben werden können. Bisher hab ich nur Zahlen und Komma zugelassen. Der Geldbetrag darf nicht mit einer 0 beginnen. Soweit funktioniert es ja schon. Nun hab ich aber weitere Anforderungen und weiss nicht, wie ich die in den Regex mit einbauen kann. Vielleicht kann mir ja wer weiterhelfen…

1.) Es soll nur 1 Komma eingegeben werden können (momentan kann man beliebig viele Kommas eingeben)
2.) Es sollen nur 2 Nachkommastellen möglich sein (die Anzahl der Nachkommastellen ist zur Zeit nicht begrenzt)

Die Klasse mit dem DocumentFilter sieht zur Zeit so aus:

extends DocumentFilter{
    
     public CurrencyFilter() {
		
     }

	
     @Override
     public void replace(FilterBypass fb, int offs, int length, String text, AttributeSet a)
          throws BadLocationException {

          // die erste Ziffer darf nicht Null sein
          if(offs == 0) {
               if (text.matches("[1-9]{1}\\d*")) {
                    super.replace(fb, offs, length, text, a);
               }			
          }
		
          // ansonsten Zahlen von 0-9 oder Komma
          else if (text.matches("[0-9,]{1}\\d*")) {
               super.replace(fb, offs, length, text, a);
          }
       
     }

}```

Das geht so: ^[1-9]\d*(,\d{2})?$
Wenn du das ? weglässt, werden auch keine Ganzzahlen zugelassen. Wenn du optional auch eine einzelne Nachkommastelle zulassen möchtest, geht das so: ^[1-9]\d+(,\d{1,2})?$

Edit: die Aussage mit dem Fragezeichen war invers formuliert.

das ist doch recht bekannter Standard
einmal 1-9, beliebig viele Ziffern, Komma als Option, zwei beliebige Ziffern

den letzten Teil evtl. als 0-2 beliebige Ziffern,
oder auch das Komma + den Rest dahinter erst als Gruppe, und diese dann zusammen als Option (edit: das ist dann das von cmrudolph)

Und wieso darf man nicht 0,50€ eingeben? Bist du sicher dass da keine 0 vorne sein darf?

Ich würde als User wieder einen Rappel bekommen wenn ich in ein Textfeld kein copy+paste benutzen kann, obwohl die Zwischenablage eig. einen korrekten/legalen String enthält…(würde ich persönlich mit beachten)

/edit nach deinem Post nach mir: Genau deswegen würde ich beide Varianten bedenken, einmal den Komplettstring und einmal jeweils die Eingabe bei einer Zahl/Komma :wink: Daher ist die alleinige Verwendung von Regex hier in meinen Augen falsch…

Aaargh, stimmt, danke. Das war eine Anforderung aus einem anderen DocumentFilter, den ich per copy&paste mit übernommen hatte. In diesem Fall brauch ich den garnicht…

Dann müsste ja das ausreichen:

	        super.replace(fb, offs, length, text, a);
	    }```

Wobei ich da schon in Eclipse die rot unterstrichene Fehlermeldung bekomme:

> Invalid escape sequence (valid ones are  \b  	  
>   \f  \r  \"  \'  \\ )


Wenn ich es so schreibe, also die Backslashes escape:
```if (text.matches("^[0-9]\\d+(,\\d{1,2})?$")) {
	        super.replace(fb, offs, length, text, a);
	    }```

kommt zwar die Fehlermeldung nicht mehr, es passiert aber auch nix, d.h. ich kann überhaupt keine Zahlen eingeben.

Ich habe meinen ersten Beitrag gerad noch einmal editiert. Statt \d+ muss es natürlich \d* heißen.

Das sollte für deine Anwendung funktionieren:

    System.out.println("match");
}```

Bist du dir sicher, dass super.replace nicht ausgeführt wird, oder entspricht das Resultat einfach nur nicht deinen Erwartungen?

[QUOTE=eRaaaa]Ich würde als User wieder einen Rappel bekommen wenn ich in ein Textfeld kein copy+paste benutzen kann, obwohl die Zwischenablage eig. einen korrekten/legalen String enthält…(würde ich persönlich mit beachten)

/edit nach deinem Post nach mir: Genau deswegen würde ich beide Varianten bedenken, einmal den Komplettstring und einmal jeweils die Eingabe bei einer Zahl/Komma :wink: Daher ist die alleinige Verwendung von Regex hier in meinen Augen falsch…[/QUOTE]

Hmm, also normalerweise sollten die User da einfach einen Geldbetrag eintippen, keine Ahnung, ob da ein copy&paste überhaupt zur Anwendung kommen könnte. Aber falls doch, wie könnte eine bessere Lösung denn dann aussehen?

*** Edit ***

[QUOTE=cmrudolph;59572]Ich habe meinen ersten Beitrag gerad noch einmal editiert. Statt \d+ muss es natürlich \d* heißen.

Das sollte für deine Anwendung funktionieren:

    System.out.println("match");
}```
[/QUOTE]

Dort steht auch wieder \\d+ anstatt \\d* . Da wird das Komma nicht akzeptiert. Aber auch nicht mit \\d*


> Bist du dir sicher, dass super.replace nicht ausgeführt wird, oder entspricht das Resultat einfach nur nicht deinen Erwartungen?


Hab auch schon Testausgaben auf der Konsole ausgeben lassen. Wird also manchmal garnicht ausgeführt...

In diesem Fall ist das Plus aber auch richtig :wink: Das liegt daran, dass ich die Zeichenklasse 1-9 davor weggelassen habe. Das Plus bedeutet, dass mindestens ein Zeichen aus der Zeichenklasse der Ziffern auftreten muss.
Je nach Quelle des zu prüfenden Strings kann es sein, dass dein Komma nur so aussieht wie eines, aber nicht mit dem im Quellcode eingegebenen übereinstimmt. Das bekommst du am einfachsten mit dem Debugger heraus.

[QUOTE=cmrudolph]In diesem Fall ist das Plus aber auch richtig :wink: Das liegt daran, dass ich die Zeichenklasse 1-9 davor weggelassen habe. Das Plus bedeutet, dass mindestens ein Zeichen aus der Zeichenklasse der Ziffern auftreten muss.
Je nach Quelle des zu prüfenden Strings kann es sein, dass dein Komma nur so aussieht wie eines, aber nicht mit dem im Quellcode eingegebenen übereinstimmt. Das bekommst du am einfachsten mit dem Debugger heraus.[/QUOTE]

OK danke, ich muss mir das nochmal genauer anschauen. Weil eigentlich ist dieser DocumentFilter Teil einer selbstgeschriebenen Klasse (abgeleitet von JTextField), in welche man Geldbeträge eingeben können soll. Eine eigene Klasse, damit ich die mit Funktionalität (Fokusweitergabe bei ENTER-Taste, Eingabemöglichkeit Betrag, Anzeige als formatierter String) und aber auch Layout (Hintergrundfarbe, Rahmen) erweitern kann. Ich wollte halt eine Klasse für Eingabe von Geldbeträgen, die ich an mehreren Stellen im Programm wiederverwenden kann. Vorher hatte ich mit double für die Geldbeträge gearbeitet und das aber jetzt auf BigDecimal (wegen der Genauigkeit) umgestellt. Ich hab das auch so, dass bei Fokuserhalt des Textfeldes ein Betrag eingegeben werden kann, der bei Fokusverlust als formatierter String (NumberFormat.getCurrencyInstance().format(value)) mit Eurozeichen dargestellt wird. Dazu hab ich aber auch den Wert als BigDecimal-Variable in dieser Klasse gespeichert, um sie nachher wieder auslesen zu können. Ist auch erstmal noch in der Entwicklungsphase. Vielleicht muss ich das Ganze nochmal neu überdenken und abändern…

*** Edit ***

Hmm, kannst du das nochmal genauer erläutern, wie das gemeint ist? Es könnte evtl. auch sein, dass es daher kommt, dass ich in meinem erstellten Textfeld je nach Fokus in einen DecimalMode (Fokuserhalt; zum eingeben von Beträgen wird der DocumentFilter gesetzt) und einem CurrencyMode (Fokusverlust; zur Anzeige des Geldbetrages formatiert mit Eurozeichen; DocumentFilter ist deaktiviert) wechsele.

Wie gesagt, es soll eine mehrfach verwendbare Komponente für mein Programm sein, damit ich relativ komfortabel Geldbeträge eingeben kann. Bis auf diesen DocumentFilter, mit dem ich erreichen möchte, dass nur bestimmte Zeichen in das Textfeld eingetragen werden können, funktioniert es ja auch schon so einigermassen (könnte noch einiges verbessert werden). Gibt es denn eine andere, bessere Möglichkeit (anstatt mit dem DocumentFilter) die möglichen Zeichen für das Textfeld festzulegen? Ist die ganze Idee mit so einem Textfeld speziell für Geldbeträge überhaupt sinnvoll? Oder gibt es da schon andere, bessere Ansätze?

Ich kann die Klasse mit dem Textfeld + DocumentFilter auch gerne mal hier reinstellen (sind insgesamt so ca. 220 Zeilen), falls gewünscht. Oder auch in den anderen Forenbereich, da es sich ja eigentlich um Swing handelt. Vielleicht gibt es ja Anregungen, Verbesserungsvorschläge (speziell zu dem DocumentFilter) oder sonstige Kritik.

Ich glaube er meint, dass rauskopieren aus dem Forum. Wenn du den RegEx hier rauskopierst und bei dir einfügst kann es sein, dass manche Zeichen anders eingefügt werden. Hab ich zum Beispiel sehr oft mit -

Häufig gibt es im UTF-8 Zeichensatz ähnliche Zeichen. Bei Bindestrichen (die sehen allerdings auch etwas unterschiedlich aus, je nach Schriftart: Gedankenstriche, Striche zwischen Ziffern wie bei 1-5 und „normale“ Bindestriche), Leerzeichen (schmale, breite, umbrechbare, nicht umbrechbare…), Apostroph (es gibt ein echtes Apostroph und viele Symbole die aussehen wie eines). Wenn aus irgendeiner Quelle ein derartiges Zeichen in deine Eingabemaske gelangt, dann sieht es zwar so aus, als ob alles korrekt wäre, aber intern ist es eben ein anderes Zeichen. Um festzustellen, welches Zeichen sich genau in der Eingabemaske befindet, bietet es sich an, im Debugger den Zeichencode anzuschauen.
Solange du die Werte nur über die Tastatur eingibst, sollte es derartige Probleme aber nicht geben.

[QUOTE=Camino;59593]Es könnte evtl. auch sein, dass es daher kommt, dass ich in meinem erstellten Textfeld je nach Fokus in einen DecimalMode (Fokuserhalt; zum eingeben von Beträgen wird der DocumentFilter gesetzt) und einem CurrencyMode (Fokusverlust; zur Anzeige des Geldbetrages formatiert mit Eurozeichen; DocumentFilter ist deaktiviert) wechsele.

Wie gesagt, es soll eine mehrfach verwendbare Komponente für mein Programm sein, damit ich relativ komfortabel Geldbeträge eingeben kann. Bis auf diesen DocumentFilter, mit dem ich erreichen möchte, dass nur bestimmte Zeichen in das Textfeld eingetragen werden können, funktioniert es ja auch schon so einigermassen (könnte noch einiges verbessert werden). Gibt es denn eine andere, bessere Möglichkeit (anstatt mit dem DocumentFilter) die möglichen Zeichen für das Textfeld festzulegen? Ist die ganze Idee mit so einem Textfeld speziell für Geldbeträge überhaupt sinnvoll? Oder gibt es da schon andere, bessere Ansätze?

Ich kann die Klasse mit dem Textfeld + DocumentFilter auch gerne mal hier reinstellen (sind insgesamt so ca. 220 Zeilen), falls gewünscht. Oder auch in den anderen Forenbereich, da es sich ja eigentlich um Swing handelt. Vielleicht gibt es ja Anregungen, Verbesserungsvorschläge (speziell zu dem DocumentFilter) oder sonstige Kritik.[/QUOTE]

Diesbezüglich kann ich keine Hilfestellung leisten (Swing ist nicht so meins…), an deiner Stelle würde ich im Swing-Forum noch einen Thread zu diesem spezifischen Problem aufmachen und hier dann kurz darauf verlinken.

OK danke. Ich dachte, ich hätte das alles per Tastatur eingegeben. Kann aber nochmal nachschauen. Ansonsten muss ich das evtl. wirklich nochmal im Swing-Forum posten. Aber wie gesagt, es hängt eigentlich nur an der Regex. Vielleicht versuche ich das nochmal aus meiner Textfeld-Klasse rauszunehmen und in kleinerem Rahmen zu testen. Danke jedenfalls für eure (sehr schnelle) Hilfe… :slight_smile:

Regex: (\d+),(\d{1,2}) ggf. am Ende noch was für die Währung anhängen z.B. (\D*) oder ([$?¥])
Java Zeichenkette: “(\d+),(\d{1,2})(\D*)”

Warum nicht http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html

Danke, ich probier das nachher mal aus und melde mich dann nochmal. Das mit der Währung ist eigenlich nicht notwendig. Die brauche ich bei der Eingabe nicht, die wird dann nachher nur nach verlassen des Textfeldes angezeigt, also der String im Textfeld formatiert (NumberFormat.getCurrencyInstance().format(value)), womit dann das Währungszeichen angezeigt wird. Beim Klick ins Textfeld (Fokuserhalt) ist der Wert dann nur als Dezimalzahl zu sehen.

*** Edit ***

Hmm danke, das mit dem JFormattedTextField hatte ich irgendwann schonmal, aber irgendwas klappte da nicht so, wie ich wollte, weswegen ich mir das damals selbst als Textfeld geschrieben hatte. Muss ich mir nochmal anschauen. Vielleicht geht es ja wirklich einfacher…

[QUOTE=Camino]Hmm, also normalerweise sollten die User da einfach einen Geldbetrag eintippen, keine Ahnung, ob da ein copy&paste überhaupt zur Anwendung kommen könnte. Aber falls doch, wie könnte eine bessere Lösung denn dann aussehen[/QUOTE]Du könntest den Betrag nach dem Druck auf “Speichern” prüfen.

Ich wollte aber schon beim Eingeben Fehler ausschliessen, also dass nur bestimmte Zeichen eingegeben werden können.

Ich bin gerade dabei, das mit einem JFormattedTextField zu versuchen. Dem kann ich ja auch das NumberFormat.getCurrencyInstance() zuweisen und mit setValue() ein BigDecimal geben. Wenn ich nun aber Beträge eingebe, verschwinden die wieder, wenn ich das Textfeld verlasse. Müssen die Werte irgendwo (z.B. in einem Model) gespeichert werden? Ich muss mir das mit dem JFormattedTextField wohl nochmal genauer anschauen, weil es ja eigentlich das gleiche macht, wie meine selbst geschriebene Klasse, die von JTextField abgeleitet war.

EDIT: OK, so langsam komm ich dahinter, wie es funktioniert. Ich muss natürlich bei einem neuen Wert auch das €-Zeichen mit reinschreiben, damit der neue Wert übernommen wird. Als Standard ist COMMIT_OR_REVERT vorgesehen, dass bei einem ungültigen Eintrag wieder auf den vorherigen Wert zurückspringt.

Schau mal hier

http://www.java2s.com/Code/Java/Swing-JFC/AquickdemonstrationofJFormattedTextField.htm

OK, so weit bin ich nun auch schon…

  • Hab dem JFormattedTextField im Konstruktor NumberFormat.getCurrencyInstance() zugewiesen; könnte ich noch mit Locale.DE versehen

  • Mit setValue() hab ich einen BigDecimal-Wert (“0.0”) ins Textfeld eingetragen, was als 0,00€ angezeigt wird

  • Das NumberFormat ist somit nur für die Anzeige/Formatierung des Wertes zuständig

  • Standardmäßig ist die Konstante COMMIT_OR_REVERT gesetzt, d.h. wenn der Eintrag falsch ist (nicht dem Format entspricht), wird der vorherige Wert wieder angezeigt

  • Dies gilt auch, wenn ich das €-Zeichen weglasse oder zwischen Betrag und €-Zeichen kein Leerzeichen ist

  • Leider kann ich auch andere Zeichen ausser Ziffern, Komma, Punkt und €-Zeichen eingeben, z.B. Buchstaben

  • Auch kann ich mehrere Kommas eingeben

Muss ich mir jetzt nur noch überlegen, ob das für den User ok, sinnvoll und zumutbar ist, oder ob ich da ne andere Funktionalität bei fehlerhaften Eingaben einbauen sollte. Aber ansonsten scheint das JFormattedTextField ja schon eine bessere Option zu sein, als mein selbstgebautes JTextField…