I have written 2 REs to match several string sequences in a String. for e.g. lets assume the two regular expressions are RE1
, RE2
. The strings can be i
You say "I know its very expensive as I am doing the matching for a particular string several times." That suggests to me that you are running each RE several times. In that case, you are making a mistake that can be resolved without writing a more complex RE.
re1_matches = re.findall(re1, text)
re2_matches = re.findall(re2, text)
This will result in two lists of matches. You can then perform boolean operations on those lists to generate whatever results you need; or you can concatenate them if you need all the matches in one list. You could also use re.match
(match anchored at beginning of string) or re.search
(match anywhere in the string) for each of these if you don't need lists of results, but only need to know that there's a match.
In any case, creating a more complex RE in this case is probably not necessary or desirable.
But it's not immediately clear to me exactly what you want, so I could be wrong about that.
Some suggestions about how to use boolean operators to process lists. First some setup:
>>> re1 = r'(\d{1,3}[a-zA-Z]?/\d{1,3}[a-zA-Z]?)'
>>> re2 = r'(\babc\b)'
>>> re.findall(re1, text)
['100/64h', '120h/90', '200/100', '100h/100f']
>>> re.findall(re2, text)
['abc', 'abc']
>>> re1_matches = re.findall(re1, text)
>>> re2_matches = re.findall(re2, text)
>>> rex_nomatch = re.findall('conglomeration_of_sandwiches', text)
and
returns the first False result or the final result if all results are True.
>>> not re1_matches and re2_matches
False
So if you want the list and not a flat boolean, you have to test the result you want last:
>>> not rex_nomatch and re1_matches
['100/64h', '120h/90', '200/100', '100h/100f']
Similarly:
>>> not rex_nomatch and re2_matches
['abc', 'abc']
If you just want to know that both REs generated matches, but don't need any more, you can do this:
>>> re1_matches and re2_matches
['abc', 'abc']
Finally, here's a compact way to get the concatenation if both REs generate matches:
>>> re1_matches and re2_matches and re1_matches + re2_matches
['100/64h', '120h/90', '200/100', '100h/100f', 'abc', 'abc']
If RE1 and RE2 could match the same characters of the string, check them separately (does RE1 match the string, does RE2 match the string).
I thought of using 'or'
|
but the problem with that is regex will stop matching once it finds the first matching sequence and not continue to find others.
That's what re.findall
is for.
>>> import re
>>> RE = r'(?:\d{1,3}[a-zA-Z]?/\d{1,3}[a-zA-Z]?)|(?:\babc\b)'
>>> string = '*some string* 100/64h *some string* 120h/90 *some string* abc 200/100 abc *some string* 100h/100f'
>>> re.findall(RE, string)
['100/64h', '120h/90', 'abc', '200/100', 'abc', '100h/100f']
Note the usage of non-capturing parentheses (the (?:...)
stuff). If the regex used capture-grouping parentheses as normal, re.findall
would return [('100/64h', ''), ('120h/90', ''), ('', 'abc'), ('200/100', ''), ('', 'abc'), ('100h/100f', '')]
.
Using |
in your regex and re.findall() is probably the way to go, here is an example:
>>> pattern = re.compile(r"(\d{1,3}[a-zA-Z]?/\d{1,3}[a-zA-Z]?|\babc\b)")
>>> pattern.findall("*some string* 100/64h *some string* 120h/90 *some string* abc 200/100 abc *some string* 100h/100f")
['100/64h', '120h/90', 'abc', '200/100', 'abc', '100h/100f']
If it is valid for your patterns to overlap then this won't work.
You need to escape the \ in the second RE:
RE1 = '(\d{1,3}[a-zA-Z]?/\d{1,3}[a-zA-Z]?)'
RE2 = '(\\babc\\b)'
s = '*some string* 100/64h *some string* 120h/90 *some string* abc 200/100 abc *some string* 100h/100f'
p = re.compile('('+RE2+'|'+RE1+')');
matches = p.findall(s)
for match in matches:
print(match[0])