【问题标题】:How to quickly get the match result of a list of strings to a list of patterns in python?如何快速获取字符串列表与python中模式列表的匹配结果?
【发布时间】:2020-10-07 01:14:30
【问题描述】:

现在我有一个模式列表:

list1 = ['a[0-9]+', 'b[a-z]+', 'c[A-Z]+', ...]

我还有另一个字符串列表:

list2 = ['a1', 'babc', 'cABC', 'bbb', 'c1', ...]

我想快速知道list2中的字符串匹配list1中的哪个模式并返回它的索引(如果不匹配则返回-1):

output = [0, 1, 2, 1, -1, ...]

现在我只需使用“for”来实现这一点:

output = []
for string in list2:
  matched = False
  for pattern in list1:
    if re.match(pattern, string):
      output.append(list1.index(pattern))
      matched = True
      break
  if not matched:
    output.append(-1)

此方法有效,但由于 list1 和 list2 很大,因此需要的时间太长。那么有没有其他方法可以快速返回结果呢?

【问题讨论】:

    标签: python python-3.x regex python-2.7


    【解决方案1】:

    尝试遍历表达式列表并对已排序的字符串列表进行二分搜索:

    import re
    list2  = ['a[0-9]+', 'b[a-z]+', 'c[A-Z]+']
    list1 = sorted(['a1', 'babc', 'cABC', 'bbb', 'c1'])
    output = []
    
    def bisect_match(l, p, start, end):
        if(end-start+1 <= 0):
            return -1
        else:
            half = start + (end - start) // 2
            if(re.match(p, l[half])):
                return half
            else:
                if(l[half] > p):
                    return(bisect_match)(l, p, start, half-1)
                else:     
                    return(bisect_match)(l, p, half+1, end)
    
    for string in list2:
        output.append(bisect_match(list1, string, 0, len(list1)))
    
    print(output)
    

    【讨论】:

    • 恐怕这不起作用,因为模式和字符串的排序不同。例如,当 list1 = ['ab1', 'a[az]+'] 和 string = 'abc' 时,它返回 -1,因为 'a[az]+' 'ab1 '。
    • 很抱歉我没有正确测试我的解决方案。切换列表是否对您有用,以便您可以对字符串而不是表达式进行排序? (见我的编辑)
    • 编辑只返回第一个匹配项,因此可能对您也不起作用 - 抱歉!
    【解决方案2】:

    您可以使用map(),这将提高嵌套循环的运行时间。
    通过减少一个if 并删除创建并附加到另一个列表并避免列表的索引方法,您将减少整体运行时间。

    import re
    
    list1 = ['a[0-9]+', 'b[a-z]+', 'c[A-Z]+']
    list2 = ['a1', 'babc', 'cABC', 'bbb', 'c1']
    
    def check_if_exist(item):
        for i, regex in enumerate(list1):
            if re.match(regex, item):
                return i
        return -1
    
    print(list(map(check_if_exist, list2)))
    

    输出

    [0, 1, 2, 1, -1]
    

    在 timeit 中检查这两个方法返回 map() 的函数在 0.17607659999999997 中运行,原始代码在 0.1822188 中运行 10000 次。
    这将对大型列表产生重大影响。

    【讨论】:

    • +1 您建议的重构很好——将逻辑移到函数中(为了清楚起见),尤其是放弃不必要的index() 使用(这肯定会加快速度)。然而,“函数式编程比循环更快”的概念大多是误导性的指导,至少没有针对手头问题的具体基准(您当前的基准混合了所有更改的效果,而不仅仅是使用 @987654327 @)。例如,我怀疑这里的列表理解会比map() 更快——但如果不对其进行基准测试,我不会断言。
    • 感谢您的说明,我很快会尝试改进解释。顺便说一句,我们将不胜感激编辑答案以获得更好的解释。
    猜你喜欢
    • 2013-06-18
    • 2013-04-24
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 2019-05-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    相关资源
    最近更新 更多