将多个正则表达式合并为一个正则表达式
我写了两个正则表达式(REs),用来匹配字符串中的几个特定序列。比如说,假设这两个正则表达式分别是 RE1
和 RE2
。这些字符串可以有四种不同的形式;
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
目前我在用 if
来检查每一种形式,但我知道这样做效率很低,因为我对同一个字符串进行了多次匹配。我想过用“或” |
来简化,但这样的话,正则表达式一旦找到第一个匹配的序列就会停止,不会继续寻找其他的。我想要找到匹配的序列“一次或多次”。
更新:
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'
我该如何合并这两个正则表达式,以提高我的程序效率呢?我正在用 Python 编写这个程序。
5 个回答
我本来想用“或”
|
来匹配,但这样做的问题是,一旦正则表达式找到第一个匹配的内容,就会停止继续查找其他的。
这就是 re.findall
的用处。
>>> 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']
注意使用了不捕获的括号(就是 (?:...)
这种)。如果正则表达式使用了普通的捕获括号,re.findall
会返回 [('100/64h', ''), ('120h/90', ''), ('', 'abc'), ('200/100', ''), ('', 'abc'), ('100h/100f', '')]
。
你需要在第二个正则表达式中对 \ 进行转义:
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])
你说“我知道这很费劲,因为我对一个特定的字符串进行了多次匹配。”这让我觉得你可能是对每个正则表达式(RE)都运行了好几次。如果是这样的话,你可能犯了一个错误,这个错误可以通过不写更复杂的正则表达式来解决。
re1_matches = re.findall(re1, text)
re2_matches = re.findall(re2, text)
这样会得到两个匹配的列表。然后你可以对这些列表进行布尔运算,生成你需要的结果;或者如果你想把所有匹配放在一个列表里,也可以把它们合并在一起。如果你只想知道是否有匹配,而不需要结果列表,你也可以使用 re.match
(从字符串开头开始匹配)或 re.search
(在字符串中任何地方匹配)。
总之,在这种情况下,创建一个更复杂的正则表达式可能不是必要的,也不一定是个好主意。
不过,我不太清楚你具体想要什么,所以我可能会错。
这里有一些关于如何使用布尔运算符来处理列表的建议。首先是一些准备工作:
>>> 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
会返回第一个为假的结果,或者如果所有结果都为真,则返回最后的结果。
>>> not re1_matches and re2_matches
False
所以如果你想要列表而不是简单的布尔值,你需要把你想要的结果放在最后进行测试:
>>> not rex_nomatch and re1_matches
['100/64h', '120h/90', '200/100', '100h/100f']
类似地:
>>> not rex_nomatch and re2_matches
['abc', 'abc']
如果你只是想知道两个正则表达式都生成了匹配,但不需要更多信息,你可以这样做:
>>> re1_matches and re2_matches
['abc', 'abc']
最后,这里有一个简洁的方法来获取合并结果,如果两个正则表达式都生成了匹配:
>>> re1_matches and re2_matches and re1_matches + re2_matches
['100/64h', '120h/90', '200/100', '100h/100f', 'abc', 'abc']