Merge several regular expressions into one RE
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 in these 4 forms;
1) Match ONLY RE1 'one or more times' 2) Match ONLY RE2 'one or more times' 3) Match RE1 'one or more times' AND match RE2 'one or more times' 4) Match NEITHER RE1 NOR RE2
currently I am using if
to check each of these, but I know its very expensive as I am doing the matching for a particular string several times. 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. I want to find matching sequences 'one or more times'.
Update:
eg: RE1 = (\d{1,3}[a-zA-Z]?/\d{1,3}[a-zA-Z]?) RE2 = (\babc\b) String: *some string* 100/64h *some string* 120h/90 *some string* abc 200/100 abc *some string* 100h/100f Matches: '100/64h', '120h/90', 'abc', '200/100', 'abc', '100h/100f'
How can I merge these 2 REs to make my program efficient. I am using p开发者_开发知识库ython to code this.
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']
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])
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.
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).
精彩评论