正则表达式中任意顺序匹配所有字符

8 投票
3 回答
11778 浏览
提问于 2025-04-17 06:17

我刚接触正则表达式,但我知道怎么在正则查询中按顺序匹配任何字符,比如说 [abc] 可以匹配 a、b 或 c 中的任意一个。而且,我相信 "abc" 会精确匹配 abc。

不过,我想知道怎么构造一个正则查询,可以匹配 abc 这几个字符,不管它们的顺序是什么。所以比如说,我希望它能匹配 "cab" 或 "bracket"。我在用 Python 作为我的脚本语言(不确定这是否重要)。

3 个回答

2

这里是对 issubset正则表达式 解决方案的时间比较。

import re

def using_lookahead(text):
    pat=re.compile(r'^(?=.*a)(?=.*b)(?=.*c)')
    return pat.search(text)

def using_set(text):
    chars=set('abc')
    return chars.issubset(text)

对于短字符串,issubset 可能会稍微快一点:

% python -mtimeit -s'import test' "test.using_set('bracket')"
100000 loops, best of 3: 2.63 usec per loop
% python -mtimeit -s'import test' "test.using_lookahead('bracket')"
100000 loops, best of 3: 2.87 usec per loop

但是对于长字符串,正则表达式明显更快:

  • 当匹配结果出现得比较晚的时候:

    % python -mtimeit -s'import test' "test.using_set('o'*1000+'bracket')"
    10000 loops, best of 3: 49.7 usec per loop
    % python -mtimeit -s'import test' "test.using_lookahead('o'*1000+'bracket')"
    100000 loops, best of 3: 6.66 usec per loop
    
  • 当匹配结果出现得比较早的时候:

    % python -mtimeit -s'import test' "test.using_set('bracket'+'o'*1000)"
    10000 loops, best of 3: 50 usec per loop
    % python -mtimeit -s'import test' "test.using_lookahead('bracket'+'o'*1000)"
    100000 loops, best of 3: 13.9 usec per loop
    

(为了回答评论中的一个问题:)可以使用 r'^(?=.*a)(?=.*b)(?=.*c)' 来表示要匹配的内容:

In [40]: pat=re.compile(r'^(?=.*a)(?=.*b)(?=.*c)')

In [41]: pat.search('bracket')
Out[41]: <_sre.SRE_Match object at 0x9f9a6b0>
12

这可以通过前瞻断言来实现:

^(?=.*a)(?=.*b)(?=.*c)

这个匹配规则会检查你的字符串中是否至少包含一次 abc

不过,正如你所看到的,这其实不是正则表达式擅长的事情。

我会这样做:

if all(char in mystr for char in "abc"):
    # do something

检查速度:

>>> timeit.timeit(stmt='chars.issubset("bracket");chars.issubset("notinhere")',
... setup='chars=set("abc")')
1.3560583674019995
>>> timeit.timeit(stmt='all(char in "bracket" for char in s);all(char in "notinhere" for char in s)', 
... setup='s="abc"')
1.4581878714681409
>>> timeit.timeit(stmt='r.match("bracket"); r.match("notinhere")', 
... setup='import re; r=re.compile("(?=.*a)(?=.*b)(?=.*c)")')
1.0582279123082117

嘿,看看,正则表达式赢了!即使是对于更长的搜索字符串,这个结果也是成立的:

>>> timeit.timeit(stmt='chars.issubset("bracketed");chars.issubset("notinhere")', 
... setup='chars=set("abcde")')
1.4316702294817105
>>> timeit.timeit(stmt='all(char in "bracketed" for char in s);all(char in "notinhere" for char in s)', 
... setup='s="abcde"')
1.6696223364866682
>>> timeit.timeit(stmt='r.match("bracketed"); r.match("notinhere")', 
... setup='import re; r=re.compile("(?=.*a)(?=.*b)(?=.*c)(?=.*d)(?:.*e)")')
1.1809254199004044
14

在Python中,我不会为了这个目的使用正则表达式,而是会用一个集合:

>>> chars = set("abc")
>>> chars.issubset("bracket")
True
>>> chars.issubset("fish")
False
>>> chars.issubset("bad")
False

正则表达式很有用,但在某些情况下,其他工具会更合适。

撰写回答