Regex match even number of letters

Viewed 31042

I need to match an expression in Python with regular expressions that only matches even number of letter occurrences. For example:

AAA        # no match
AA         # match
fsfaAAasdf # match
sAfA       # match
sdAAewAsA  # match
AeAiA      # no match

An even number of As SHOULD match.

8 Answers

Try this regular expression:

^[^A]*((AA)+[^A]*)*$

And if the As don’t need to be consecutive:

^[^A]*(A[^A]*A[^A]*)*$

This searches for a block with an odd number of A's. If you found one, the string is bad for you:

(?<!A)A(AA)*(?!A)

If I understand correctly, the Python code should look like:

if re.search("(?<!A)A(AA)*(?!A)", "AeAAi"):
   print "fail"

Why work so hard coming up with a hard to read pattern? Just search for all occurrences of the pattern and count how many you find.

len(re.findall("A", "AbcAbcAbcA")) % 2 == 0

That should be instantly understandable by all experienced programmers, whereas a pattern like "(?

Simple is better.

'A*' means match any number of A's. Even 0.

Here's how to match a string with an even number of a's, upper or lower:

re.compile(r'''
    ^
    [^a]*
    (
        (
            a[^a]*
        ){2}
    # if there must be at least 2 (not just 0), change the
    # '*' on the following line to '+'
    )* 
    $
    ''',re.IGNORECASE|re.VERBOSE)

You probably are using a as an example. If you want to match a specific character other than a, replace a with %s and then insert

[...]
$
'''%( other_char, other_char, other_char )
[...]

'*' means 0 or more occurences 'AA' should do the trick.

The question is if you want the thing to match 'AAA'. In that case you would have to do something like:

r = re.compile('(^|[^A])(AA)+(?!A)',)
r.search(p)

That would work for match even (and only even) number of'A'.

Now if you want to match 'if there is any even number of subsequent letters', this would do the trick:

re.compile(r'(.)\1')

However, this wouldn't exclude the 'odd' occurences. But it is not clear from your question if you really want that.

Update: This works for you test cases:

re.compile('^([^A]*)AA([^A]|AA)*$')

First of all, note that /A*/ matches the empty string.

Secondly, there are some things that you just can't do with regular expressions. This'll be a lot easier if you just walk through the string and count up all occurences of the letter you're looking for.

A* means match "A" zero or more times.

For an even number of "A", try: (AA)+

It's impossible to count arbitrarily using regular expressions. For example, making sure that you have matching parenthesis. To count you need 'memory' which requires something at least as strong as a pushdown automaton, although in this case you can use the regular expression that @Gumbo provided.

The suggestion to use finditeris the best workaround for the general case.

Related