Reputation: 1178
I need to validate an input string such that validation returns true
only if the string contains one of the special characters @ # $ %, only one, and one time at the most. Letters and numbers can be anywhere and can be repeated any number of times, but at least one number or letter should be present
For example:
a@ : true
@a : true
a@$: false
a@n01 : true
an01 : false
a : false
@ : false
I tried
[0-9A-Za-z]*[@#%$]{1}[0-9A-Za-z]*
I was hoping this would match one occurrence of any of the special characters. But, no. I need only one occurrence of any one in the set.
I also tried alternation but could not solve it.
Upvotes: 9
Views: 19432
Reputation: 41838
Vivek, your regex was really close. Here is the one-line regex you are looking for.
^(?=.*?[0-9a-zA-Z])[0-9a-zA-Z]*[@#$%][0-9a-zA-Z]*$
See demo
How does it work?
^
and $
anchors ensure that whatever we are matching is the whole string, avoiding partial matches with forbidden characters later.(?=.*?[0-9a-zA-Z])
lookahead ensures that we have at least one number or letter.[0-9a-zA-Z]*[@#$%][0-9a-zA-Z]*
matches zero or more letters or digits, followed by exactly one character that is either a @
, #
, $
or %
, followed by zero or more letters or digits—ensuring that we have one special character but no more.Implementation
I am sure you know how to implement this in Java, but to test if the string match, you could use something like this:
boolean foundMatch = subjectString.matches("^(?=[0-9a-zA-Z]*[@#$%][0-9a-zA-Z]*$)[@#$%0-9a-zA-Z]*");
What was wrong with my regex?
Actually, your regex was nearly there. Here is what was missing.
^
and $
anchors, the regex was able to match a subset of the string, for instance a#
in a##%%
, which means that special characters could appear in the string, but outside of the match. Not what you want: we need to validate the whole string by anchoring it.Alternative with Alternation
Since you tried alternations, for the record, here is one way to do it:
^(?:[0-9a-zA-Z]+[@#$%][0-9a-zA-Z]*|[0-9a-zA-Z]*[@#$%][0-9a-zA-Z]+)$
See demo.
Let me know if you have any questions.
Upvotes: 13
Reputation: 69269
I hope this answer will be useful for you, if not, it might be for future readers. I am going to make two assumptions here up front: 1) You do not need regex per se, you are programming in Java. 2) You have access to Java 8.
This could be done the following way:
private boolean stringMatchesChars(final String str, final List<Character> characters) {
return (str.chars()
.filter(ch -> characters.contains((char)ch))
.count() == 1);
}
Here I am:
String
and a List<Character>
of the ones that are allowed.IntStream
(consisting of chars) from the String
.char
to only remain in the stream if they are in the List<Character>
.true
only if the count() == 1
, that is of the characters in List<Character>
, exactly one is present.The code can be used as:
String str1 = "a";
String str2 = "a@";
String str3 = "a@@a";
String str4 = "a#@a";
List<Character> characters = Arrays.asList('@', '#', '$', '%');
System.out.println("stringMatchesChars(str1, characters) = " + stringMatchesChars(str1, characters));
System.out.println("stringMatchesChars(str2, characters) = " + stringMatchesChars(str2, characters));
System.out.println("stringMatchesChars(str3, characters) = " + stringMatchesChars(str3, characters));
System.out.println("stringMatchesChars(str4, characters) = " + stringMatchesChars(str4, characters));
Resulting in false
, true
, false
, false
.
Upvotes: 2