I have been trying to figure out a REGEX expressions which can filter characters which are not '-' or 0-9. This expression will be used with a document filter which will filter characters being inserted into JTextFields. Let me explain in more detail...
When a user enters a character into a JTextField a DocumentFilter checks the input using the replace
method in the DocumentFilter class. Because the method is called every time a character is inserted the REGEX needed to be able to handle parts of an integer as the user builds the string. For example,
Key Press 1): Value = '-' PASS
Key Press 2): Value = '-1' PASS
Key Press 3): Value = '-10' PASS etc...
However the filter should not allow the combination '-0' or '--' and should pass the following cases,
Negative Symbol Only ('-')
Negative Number Without Zero Next To '-' ('-01' fail)
Positive Values (0122 and 122 pass)
Here is my code:
package regex;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class CustomFilter extends DocumentFilter {
private String regex = "((-?)([1-9]??))(\d)";
/**
* Called every time a new character is added.
*/
@Override
public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
String text = fb.getDocument().getText(0, fb.getDocument().getLength());
text += string;
if(text.matches(regex)) {
super.insertString(fb, offset, string, attr);
}
}
/**
* Called when the user pastes in new text.
*/
@Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
String string = fb.getDocument().getText(0, fb.getDocument().getLength());
string += text;
if(string.matches(regex)) {
super.replace(fb, offset, length, text, attrs);
}
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length); // I may modify this later
}
}
If you believe I am going about this in the wrong direction please let me know. I am open to a more simplistic option.
Instead of all the alternations, factor it down to this
"^(?=[-\\d])(?:-(?!0))?\\d*$"
Explained
^ # Beginning of string
(?= [-\d] ) # Must be a character in it
(?: # Optional minus sign
-
(?! 0 ) # If not followed by 0
)?
\d* # Optional digits [0-9]
$ # End of string
Mod:
With a little extra effort, you could get the partial valid string in
capture group 1 and any remaining invalid trailing chars in group 2.
All this requires is to test if there is a match.
Of course you need the matcher to get the groups.
Possible outcomes:
"^(?=.)((?:-(?>(?=0)|\\d*)|\\d*))(.*)$"
https://regex101.com/r/oxmZfa/1
Explained
^ # Beginning of string
(?= . ) # Must be a character in the string
( # (1 start), Template for pattial validity
(?:
- # The case for minus
(?> # Atomic group for safety
(?= 0 ) # Don't capture 0 if it's ahead
| # or,
\d* # Any digits, 0 won't be first
)
| # or, the case for No minus
\d* # Any digits
)
) # (1 end)
( .* ) # (2), This is to be trimmed, stuff here doesn't match the template
$ # End of string
When the user submits the text use this regex to validate the input.
If it doesn't match, the user did not add numbers to the field.
Just popup a message that the input is incomplete.
"^(?=[-\\d])(?:-(?!0))?\\d+$"