Rino Bino
Rino Bino

Reputation: 447

Regex: Match string from set of characters, but need to have exactly X count of a specific character

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

Answers (3)

The fourth bird
The fourth bird

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 string

Regex demo

Or a shorter version using a quantifier based of the answer of @MonkeyZeus.

^(?=[ple]*a)(?=[ape]*l)(?=[apl]*e)(?=[ale]*p[ale]*p)[aple]{5}$

Regex demo

Upvotes: 2

agent-j
agent-j

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

MonkeyZeus
MonkeyZeus

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 ps 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 modifier

https://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

Related Questions