【问题标题】:Efficient way of matching and replacing multiple strings in python 3?在python 3中匹配和替换多个字符串的有效方法?
【发布时间】:2017-06-13 17:42:12
【问题描述】:

我有多个 (>30) 已编译的正则表达式

regex_1 = re.compile(...)
regex_2 = re.compile(...)
#... define multiple regex's
regex_n = re.compile(...)

然后我有一个函数,它接受 text 并使用上面的每一个正则表达式和 re.sub 方法替换它的一些单词,如下所示

def sub_func(text):
    text = re.sub(regex_1, "string_1", text)
    # multiple subsitutions using all regex's ...
    text = re.sub(regex_n, "string_n", text)

    return text

问题:有没有更有效的方法来进行这些替换?

正则表达式不能从其当前形式概括或简化。

我觉得每次为每个正则表达式重新分配 text 的值非常慢,因为该函数每次重新分配时只替换整个 text 中的一两个单词。此外,考虑到我必须为多个文档执行此操作,这会进一步减慢速度。

提前致谢!

【问题讨论】:

  • 如果这些替换是互斥的,您可能会发现使用re.sub(re.compile("pattern1|pattern2|..."), function_that_handles_match, text) 会更快。 (当然是未经检验的假设:)
  • @Pythonista 如果他想为每个正则表达式替换一个单独的字符串就行不通
  • 当您开始命名变量xxx_1xxx_2 等时,这表明您确实想将这些变量收集到一个列表中。使添加或重新排序变得更加容易,并且迭代列表比复制/粘贴do_this_to(xxx_1); do_this_to(xxx_2); do_this_to(xxx_2a_which_i_needed_between_2_and_3_but_didnt_want_to_renumber_everything); do_this_to(xxx_3); etc. 更简单
  • 您是否看到随着正则表达式数量的增加而性能下降? (即你不是想过早地优化它吗?)
  • 1.如果您向我们展示一些典型的正则表达式和典型的替换字符串,将会有所帮助。 2. 这些文件有多大?在部分(行、段落、页面)中处理文档可能比在大型文档中执行多个字符串替换更有效。每次替换都涉及分配一个新字符串,当您对一个大字符串进行多次替换时,这会变得很昂贵。

标签: python regex nlp python-3.6


【解决方案1】:

我们可以将函数传递给re.subrepl 参数

简化为 3 个正则表达式以便于理解

假设 regex_1、regex_2 和 regex_3 将分别为 111,222 和 333。然后,regex_replace 将是包含将用于替换的字符串列表,按照 regex_1、regex_2 和 regex_3 的顺序。

  • regex_1 将被替换为“一”
  • regex_2 替换为 'two' 等等

不确定这会在多大程度上改善运行时,试一试

import re
regex_x = re.compile('(111)|(222)|(333)')
regex_replace = ['one', 'two', 'three']

def sub_func(text):
    return re.sub(regex_x, lambda x:regex_replace[x.lastindex-1], text)

>>> sub_func('testing 111 222 333')
>>> 'testing one two three'

【讨论】:

    【解决方案2】:

    在 Python 中重新分配一个值需要固定的时间。与 C 等语言不同,变量更像是“名称标签”。因此,更改名称标签所指向的内容需要很短的时间。

    如果它们是常量字符串,我会将它们收集到一个元组中:

    regexes = (
        (regex_1, 'string_1'),
        (regex_2, 'string_2'),
        (regex_3, 'string_3'),
        ...
    )
    

    然后在你的函数中,遍历列表:

    def sub_func_2(text):
        for regex, sub in regexes:
            text = re.sub(regex, sub, text)
        return text
    

    但如果您的正则表达式实际上命名为 regex_1regex_2 等,它们可能应该直接在某种列表中定义。

    另请注意,如果您正在执行 'cat' -> 'dog' 之类的替换,str.replace() 方法可能更简单 (text = text.replace('cat', 'dog')),而且可能会更快。


    如果您的字符串 非常 长,并且使用正则表达式从头开始重新制作它可能需要很长时间。 cmets 中提到的@Oliver Charlesworth's 方法的实现可能是:

    # Instead of this:
    regexes = (
       ('1(1)', '$1i'),
       ('2(2)(2)', '$1a$2'),
       ('(3)(3)3', '$1a$2')
    )
    
    
    # Merge the regexes:
    regex = re.compile('(1(1))|(2(2)(2))|((3)(3)3)')
    substitutions = (
        '{1}i', '{1}a{2}', '{1}a{2}'
    )
    
    # Keep track of how many groups are in each alternative
    group_nos = (1, 2, 2)
    
    cumulative = [1]
    for i in group_nos:
        cumulative.append(cumulative[-1] + i + 1)
    del i
    cumulative = tuple(zip(substitutions, cumulative))
    
    def _sub_func(match):
        iter_ = iter(cumulative)
        for sub, x in iter_:
            if match.group(x) is not None:
                return sub.format(*map(match.group, range(x, next(iter_)[1])))
    
    def sub_func(text):
        return re.sub(regex, _sub_func, text)
    

    但是,如果您有重叠的文本需要替换,这就会失败。

    【讨论】:

    • 我认为 OP 并不担心分配参考所花费的时间;而是执行 30 次独立字符串替换所花费的时间(这需要重建字符串 30 次)。
    • 感谢@Artyer,这确实清理了我的代码,因为它使我不必重复代码(定义正则表达式,然后在我的函数中逐个替换单词)。 tuple-for 循环方法要简洁得多。由于正则表达式挑选出非常具体的实体,因此无法进一步概括它们。此外,字符串替换都是不同的,所以我也不能用一个字符串替换它们。再次感谢!
    猜你喜欢
    • 2013-02-14
    • 1970-01-01
    • 2016-09-26
    • 2014-03-12
    • 2018-03-28
    • 1970-01-01
    • 2012-03-06
    • 1970-01-01
    相关资源
    最近更新 更多