Reputation: 447
I have a very specific regex match and I can't seem to figure it out.
I need to find full strings matches for /^[aple]+$/i
but MUST match exactly two "p's" and exactly one of the other characters.
Any other characters are not allowed. Case insensitive.
So, for example, these will successfully match:
Apple
ppleA
APPLE
apple
ppale
These would not match:
foo
appple
aple
bar
test
I've tried to set the explicit count of p
using {brackets} but I think I have the syntax completely wrong. Such as: /^([p]{2})([ale]{1}+$)/i
-- not working at all. This one ^(([a]{1})([p]{2})([l]{1})([e]{1}))+$
matches "apple" but it doesn't match "pplea"
Assume I can do this in two sets of matches, run it first to get the set of matches that may have any # of p's and then do a second run to test for exactly two, but there has got to be a one-liner?
Thanks for any help.
Upvotes: 0
Views: 5853
Reputation: 163362
You can assert a single instance of a
l
and e
by using a character class and optionally matching all allowed chars except the one that you want to assert, preventing unnecessary backtracking.
Then match 2 times a p
char between optionally matching the other chars (as they are already asserted for).
^(?=[ple]*a[ple]*$)(?=[ape]*l[ape]*$)(?=[apl]*e[apl]*$)[ale]*p[ale]*p[ale]*$
Explanation
^
Start of string(?=[ple]*a[ple]*$)
Assert an a(?=[ape]*l[ape]*$)
Assert an l(?=[apl]*e[apl]*$)
Assert an e[ale]*p[ale]*p[ale]*
Match 2 times a p
$
End of stringOr a shorter version using a quantifier based of the answer of @MonkeyZeus.
^(?=[ple]*a)(?=[ape]*l)(?=[apl]*e)(?=[ale]*p[ale]*p)[aple]{5}$
Upvotes: 2
Reputation: 27923
^(?=^[^a]*a[^a]*$)(?=^[^l]*l[^l]*$)(?=^[^e]*e[^e]*$)[aple]{5}$
This works by restricting the number of a
, l
, and e
characters to exactly 1 of each, and then restricting the overall string to exactly 5 characters to enforce 2 p
characters.
You asked about regex, but in case it helps, an algorithmic version would be to sort the characters of APPLE
and compare to the upper-cased version of your string sorted as above.
Upvotes: 1
Reputation: 20737
Something like this would work:
/^(?=.*a)(?=.*p.*p)(?=.*l)(?=.*e)[aple]{5}$/i
/^
- start the regex and use a start string anchor(?=.*a)
- ensure that an a
exists anywhere in the string(?=.*p.*p)
- ensure that two p
s exist anywhere in the string(?=.*l)
- ensure that an l
exists anywhere in the string(?=.*e)
- ensure that an e
exists anywhere in the string[aple]{5}
- get 5 chars of a
, p
, l
, or e
$
- end string anchor/i
- case-insensitive modifierhttps://regex101.com/r/DJlWAL/1/
You can ignore the /gm
in the demo as they are used just for show with all the words at one time.
Upvotes: 3