具有不同字符数的正则表达式

时间:2018-07-01 15:28:06

标签: python regex

我需要创建一个正则表达式来验证字符串。字符串只能包含几个字符,每个字符只能重复几次。

正则表达式应检查以下条件。

  1. 字符串只能包含 a,b,c,d,e 作为字符。
  2. 字符“ a ”最多可以出现2次。
  3. 字符“ b ”最多可以出现3次。
  4. 字符“ c ”最多可以出现3次。
  5. 字符“ d ”最多可以出现1次。
  6. 字符“ e ”最多可以出现1次。

我知道这可以通过字符串函数来实现。但是我正在尝试使用正则表达式。

在此方面的任何帮助都将受到高度赞赏。

4 个答案:

答案 0 :(得分:4)

同样,出于性能考虑,最好的方法是使用Python本机字符串操作。

我会这样写:

lim=(('a',2),('b',3),('c',3),('d',1),('e',1))
results={}
for s in [list_of_many_strings]:
    results[s]=bool(not(set(s)-set('abcde'))) and (not any(s.count(c)>x for c,x in lim))

这依靠str.count(sub[, start[, end]])来计算字符串中子字符串的出现,并依靠any function来测试是否满足任何条件。


由于您对性能感兴趣,因此可以确定使用timeit处理100,000个字符串可能要花费多长时间:

import re

def f1(li):
    results={}
    lim=(('a',2),('b',3),('c',3),('d',1),('e',1))
    for s in li:
        results[s]=bool(not(set(s)-set('abcde'))) and (not any(s.count(c)>x for c,x in lim))

    return results    

def f2(li):
    pat=re.compile(r'^a{0,2}b{0,3}c{0,3}d{0,1}e{0,1}$')
    results={}
    for s in li:
        results[s]=True if pat.search(''.join(sorted(s))) else False

    return results

def f3(li):
    pat=re.compile(r'^(?!.*[^a-e])(?!(?:.*a){3})(?!(?:.*b){4})(?!(?:.*c){4})(?!(?:.*d){2})(?!(?:.*e){2}).+')
    results={}
    for s in li:
        results[s]=True if pat.search(s) else False    

    return results    

if __name__=='__main__':
    import timeit    
    import random 
    s='abcdeabcdebc'
    li=[''.join(random.sample(s,8)) for _ in range(100000)]
    print(f1(li)==f2(li)==f3(li))

    for f in (f1,f2,f3):
        print("   {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("f(li)", setup="from __main__ import f, li", number=10)))

在我的计算机上,需要:

True
       f1    0.8519 secs
       f2    1.1235 secs
       f3    1.3070 secs

答案 1 :(得分:3)

可以通过以下方式构造检查多个条件的正则表达式:

  • ^-源字符串的开头。
  • 针对任何“必需”条件的一系列正查询。
  • 针对任何“禁止”条件的一系列否定查询。
  • .+-如果先前的所有查找均成功,则匹配整个(通常 非空)源字符串。

如果正向或负向查找都指向一个字符 任意位置,它应以.*开头, 指出在我们实际检查之前,可能会发生任何 任何(其他)字符的数量,可能没有。

您的案件实际上仅包含“禁止”条件,说明 这是不允许的:

  • (?!.*[^a-e])-除a-e以外的任何字符。
  • (?!(?:.*a){3})-a出现3次(或更多)。
  • (?!(?:.*b){4})-b出现4次(或更多)。
  • (?!(?:.*c){4})-c出现4次(或更多)。
  • (?!(?:.*d){2})-d出现2次(或更多)。
  • (?!(?:.*e){2})-e出现2次(或更多)。

因此整个正则表达式应为:

^(?!.*[^a-e])(?!(?:.*a){3})(?!(?:.*b){4})(?!(?:.*c){4})(?!(?:.*d){2})(?!(?:.*e){2}).+

答案 2 :(得分:2)

如果您的字符串已经排序,例如所有a字符已经在所有b字符的前面,依此类推,则像这样的简单正则表达式将起作用:

r'^a{0,2}b{0,3}c{0,3}d{0,1}e{0,1}$'

如果字符串中的字符未排序,那么,请先对它们进行排序=)

并且如果您的“最多可以出现2次”的含义是1或2次(如我所料,不是0、1或2),则将0中的所有1替换为{ reg.expression。

答案 3 :(得分:1)

由于您使用的是熊猫,因此最好使用矢量化操作。尽管我不愿意检查,但这些应该更快。这是一种可能的方法,熊猫或numpy专家可能有更好的方法:

import random

import pandas as pd

s = 'abcdeabcdebc'
df = pd.DataFrame({'s': [''.join(random.sample(s, 8)) for _ in range(100000)]})
count = df.s.str.count
df['valid'] = ((count('[^a-e]') == 0) &
               (count('a') <= 2) &
               (count('b') <= 3) &
               (count('c') <= 3) &
               (count('d') <= 1) &
               (count('e') <= 1))

print(df.head())

示例输出:

          s  valid
0  cacbdbec   True
1  cabdceab   True
2  bbdcdabe  False
3  ecaadbce  False
4  ebabcdad  False