如何使用正则表达式进行多次替换?

Posted

技术标签:

【中文标题】如何使用正则表达式进行多次替换?【英文标题】:How can I do multiple substitutions using regex? 【发布时间】:2013-02-16 23:33:52 【问题描述】:

我可以使用下面的代码创建一个新文件,并使用正则表达式将a 替换为aa

import re

with open("notes.txt") as text:
    new_text = re.sub("a", "aa", text.read())
    with open("notes2.txt", "w") as result:
        result.write(new_text)

我想知道我是否必须多次使用new_text = re.sub("a", "aa", text.read()) 这一行,但用该字符串替换我想要更改的其他字母,以便在我的文本中更改多个字母?

也就是说,a-->aa,b--> bbc--> cc

所以我必须为我想要更改的所有字母写那行,或者有更简单的方法。也许是为了创建一个翻译“词典”。我应该将这些字母放入数组中吗?如果我这样做,我不知道如何打电话给他们。

【问题讨论】:

【参考方案1】:

如果您的模式本身就是正则表达式,则其他解决方案都不起作用。

为此,您需要:

def multi_sub(pairs, s):
    def repl_func(m):
        # only one group will be present, use the corresponding match
        return next(
            repl
            for (patt, repl), group in zip(pairs, m.groups())
            if group is not None
        )
    pattern = '|'.join("()".format(patt) for patt, _ in pairs)
    return re.sub(pattern, repl_func, s)

可以用作:

>>> multi_sub([
...     ('a+b', 'Ab'),
...     ('b', 'B'),
...     ('a+', 'A.'),
... ], "aabbaa")  # matches as (aab)(b)(aa)
'AbBA.'

请注意,此解决方案不允许您将捕获组放入您的正则表达式中,或在替换中使用它们。

【讨论】:

【参考方案2】:

如果你处理文件,我有一个关于这个问题的简单 python 代码。 更多信息here。

import re 

 def multiple_replace(dictionary, text):
  # Create a regular expression  from the dictionaryary keys

  regex = re.compile("(%s)" % "|".join(map(re.escape, dictionary.keys())))

  # For each match, look-up corresponding value in dictionaryary
  String = lambda mo: dictionary[mo.string[mo.start():mo.end()]]
  return regex.sub(String , text)


if __name__ == "__main__":

dictionary = 
    "Wiley Online Library" : "Wiley",
    "Chemical Society Reviews" : "Chem. Soc. Rev.",
 

with open ('LightBib.bib', 'r') as Bib_read:
    with open ('Abbreviated.bib', 'w') as Bib_write:
        read_lines = Bib_read.readlines()
        for rows in read_lines:
            #print(rows)
            text = rows
            new_text = multiple_replace(dictionary, text)
            #print(new_text)
            Bib_write.write(new_text)

【讨论】:

【参考方案3】:

您可以使用pandas 库和replace 函数。我用五个替换来代表一个例子:

df = pd.DataFrame('text': ['Billy is going to visit Rome in November', 'I was born in 10/10/2010', 'I will be there at 20:00'])

to_replace=['Billy','Rome','January|February|March|April|May|June|July|August|September|October|November|December', '\d2:\d2', '\d2/\d2/\d4']
replace_with=['name','city','month','time', 'date']

print(df.text.replace(to_replace, replace_with, regex=True))

而修改后的文字是:

0    name is going to visit city in month
1                      I was born in date
2                 I will be there at time

你可以找到例子here

【讨论】:

在我的情况下,这比直接使用正则表达式效率低,也许在某些情况下不是这样? 如果您想使用矢量化在 pandas 数据框中一次应用多个替换【参考方案4】:

我发现我必须修改 Emmett J. Butler 的代码,将 lambda 函数更改为使用 myDict.get(mo.group(1),mo.group(1))。原始代码对我不起作用;如果找不到键,使用 myDict.get() 还可以提供默认值。

OIDNameContraction = 
                                'Fucntion':'Func',
                                'operated':'Operated',
                                'Asist':'Assist',
                                'Detection':'Det',
                                'Control':'Ctrl',
                                'Function':'Func'


replacementDictRegex = re.compile("(%s)" % "|".join(map(re.escape, OIDNameContraction.keys())))

oidDescriptionStr = replacementDictRegex.sub(lambda mo:OIDNameContraction.get(mo.group(1),mo.group(1)), oidDescriptionStr)

【讨论】:

【参考方案5】:

使用来自how to make a 'stringy' class 的提示,我们可以使对象与字符串相同,但需要额外的sub 方法:

import re
class Substitutable(str):
  def __new__(cls, *args, **kwargs):
    newobj = str.__new__(cls, *args, **kwargs)
    newobj.sub = lambda fro,to: Substitutable(re.sub(fro, to, newobj))
    return newobj

这允许使用构建器模式,它看起来更好,但仅适用于预定数量的替换。如果您在循环中使用它,那么创建额外的类就没有意义了。例如

>>> h = Substitutable('horse')
>>> h
'horse'
>>> h.sub('h', 'f')
'forse'
>>> h.sub('h', 'f').sub('f','h')
'horse'

【讨论】:

【参考方案6】:

@nhahtdh 提出的答案是有效的,但我认为它不像规范示例那样 Python 化,它使用的代码比他的正则表达式操作更不透明,并利用了 python 的内置数据结构和匿名函数特性。

翻译词典在这种情况下是有意义的。事实上,Python Cookbook 就是这样做的,如本例所示(复制自 ActiveState http://code.activestate.com/recipes/81330-single-pass-multiple-replace/)

import re 

def multiple_replace(dict, text):
  # Create a regular expression  from the dictionary keys
  regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))

  # For each match, look-up corresponding value in dictionary
  return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) 

if __name__ == "__main__": 

  text = "Larry Wall is the creator of Perl"

  dict = 
    "Larry Wall" : "Guido van Rossum",
    "creator" : "Benevolent Dictator for Life",
    "Perl" : "Python",
   

  print multiple_replace(dict, text)

因此,在您的情况下,您可以创建一个 dict trans = "a": "aa", "b": "bb",然后将其与您要翻译的文本一起传递给 multiple_replace。基本上,该函数所做的只是创建一个包含所有要翻译的正则表达式的巨大正则表达式,然后当找到一个时,将 lambda 函数传递给 regex.sub 以执行翻译字典查找。

您可以在读取文件时使用此功能,例如:

with open("notes.txt") as text:
    new_text = multiple_replace(replacements, text.read())
with open("notes2.txt", "w") as result:
    result.write(new_text)

我实际上在生产中使用了这种精确的方法,在我需要将一年中的月份从捷克语翻译成英语以完成网络抓取任务的情况下。

正如@nhahtdh 指出的那样,这种方法的一个缺点是它不是无前缀的:作为其他字典键前缀的字典键将导致方法中断。

【讨论】:

哇,谢谢,这正是我想要的。我还有一个基本问题,如何忽略大写字母?因此,如果我有 A 并且我还想将其翻译为 aa 而无需将其添加到字典中。 @Euridice01:如果要忽略大小写,请在re.compile 中指定re.I 标志。 您当前的解决方案尚未针对存在一对单词的用例进行配置,其中一个单词是另一个单词的前缀。交替事项中出现的顺序。我认为至少你应该陈述这个假设。 我无法让 re:I 在这种情况下工作(根据@nhahtdh 的建议) Penny:我看不出在这种情况下如何使用通配符。我试过了,没有成功。 @thescoop:用您的代码提出一个新问题。如果要在地图中使用正则表达式,则需要重写函数以删除编译中的 re.escape 并更改自定义替换函数以查找负责匹配的组并查找相应的替换(在这种情况下,输入应该是一个元组数组而不是字典)。【参考方案7】:

您可以使用捕获组和反向引用:

re.sub(r"([characters])", r"\1\1", text.read())

[] 之间加上你想加倍的字符。对于小写abc的情况:

re.sub(r"([abc])", r"\1\1", text.read())

在替换字符串中,您可以使用\n 表示法引用捕获组() 匹配的任何内容,其中n 是某个 整数(0 除外)。 \1 指的是第一个捕获组。还有另一种表示法\g<n>,其中n 可以是任何非负整数(允许为0); \g<0> 将引用表达式匹配的整个文本。


如果你想将除换行之外的所有字符加倍:

re.sub(r"(.)", r"\1\1", text.read())

如果你想将所有字符加倍(包括新行):

re.sub(r"(.)", r"\1\1", text.read(), 0, re.S)

【讨论】:

以上是关于如何使用正则表达式进行多次替换?的主要内容,如果未能解决你的问题,请参考以下文章

通过正则表达式模式匹配使用 stringbuilder 替换多次出现的字符串

正则表达式 - 替换多次出现

正则表达式高级替换,匹配后进行运算,然后使用结果替换,怎么实现?

Oracle 正则表达式替换用逗号包围的字符串的多次出现

正则表达式 - 替换多次出现

[技]如何在 notepad++ 里面使用正则表达式进行替换