ChatGPT解决这个技术问题 Extra ChatGPT

如何替换字符串的多个子字符串?

我想使用 .replace 函数来替换多个字符串。

我目前有

string.replace("condition1", "")

但想要有类似的东西

string.replace("condition1", "").replace("condition2", "text")

虽然这感觉不是很好的语法

这样做的正确方法是什么?有点像在 grep/regex 中您可以执行 \1\2 将字段替换为某些搜索字符串

您是否尝试了所有提供的解决方案?哪个更快?
我花时间测试了不同场景下的所有答案。请参阅stackoverflow.com/questions/59072514/…
老实说,与其他所有方法相比,我更喜欢您的链式方法。我在寻找解决方案时登陆这里并使用了您的解决方案,它工作得很好。
@frakman1 +1。不知道为什么这没有得到更多的支持。所有其他方法使代码更难阅读。如果有要替换的函数传递数组,这将起作用。但是你的链式方法是最清楚的(至少有固定数量的替换)
简短的回答似乎是:没有更好的方法来做到这一点。

M
Majid Ali Khan

这是一个简短的示例,应该使用正则表达式来解决问题:

import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems()) 
#Python 3 renamed dict.iteritems to dict.items so use rep.items() for latest versions
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)

例如:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'

替换发生在一次通过中。
dkamins:它不太聪明,甚至没有应有的聪明(我们应该在用“|”加入键之前对键进行正则表达式转义)。为什么不是过度设计?因为这样我们一次性完成(=fast),并且我们同时进行所有替换,避免像 "spamham sha".replace("spam", "eggs").replace("sha","md5")"eggmd5m md5" 而不是 "eggsham md5" 这样的冲突
@AndrewClark 如果您能用 lambda 解释最后一行发生的事情,我将不胜感激。
您好,我创建了一个小要点,其中包含该片段的更清晰版本。它也应该稍微更有效:gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
对于 python 3,使用 items() 而不是 iteritems()。
r
root

你可以做一个不错的小循环函数。

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text

其中 text 是完整的字符串,而 dic 是一个字典——每个定义都是一个字符串,它将替换与该术语的匹配项。

注意:在 Python 3 中,iteritems() 已替换为 items()

小心:Python 字典没有可靠的迭代顺序。此解决方案仅在以下情况下解决您的问题:

替换顺序无关紧要

替换可以改变之前替换的结果

更新:上述与插入排序相关的语句不适用于大于或等于 3.6 的 Python 版本,因为标准 dicts 已更改为使用插入排序进行迭代。

例如:

d = { "cat": "dog", "dog": "pig"}
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, d)
print(my_sentence)

可能的输出 #1:

"This is my pig and this is my pig."

可能的输出 #2

"This is my dog and this is my pig."

一种可能的解决方法是使用 OrderedDict。

from collections import OrderedDict
def replace_all(text, dic):
    for i, j in dic.items():
        text = text.replace(i, j)
    return text
od = OrderedDict([("cat", "dog"), ("dog", "pig")])
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, od)
print(my_sentence)

输出:

"This is my pig and this is my pig."

小心 #2: 如果您的 text 字符串太大或字典中有很多对,则效率会降低。


应用不同替换的顺序很重要 - 因此不要使用标准字典,而是考虑使用 OrderedDict - 或 2 元组列表。
这使得迭代字符串两次......不利于表演。
就性能而言,它比 Valentin 所说的更糟糕 - 它会遍历文本的次数与 dic 中的项目一样多!如果“文本”很小,那很好,但对于大文本来说很糟糕。
请注意,这可能会产生意想不到的结果,因为在第一次迭代中新插入的文本可以在第二次迭代中匹配。例如,如果我们天真地尝试用“B”替换所有“A”,用“C”替换所有“B”,则字符串“AB”将被转换为“CC”,而不是“BC”。
注意:从 Python 3.7 开始,“dict 对象的插入顺序保存特性已被声明为 Python 语言规范的官方部分。” -- 3.7 Release Notes
S
Snehal Parmar

为什么没有这样的解决方案?

s = "The quick brown fox jumps over the lazy dog"
for r in (("brown", "red"), ("lazy", "quick")):
    s = s.replace(*r)

#output will be:  The quick red fox jumps over the quick dog

这非常有用,简单且便携。
看起来不错,但不能像 in: for r in ((r'\s.', '.'), (r'\s,' , ',')) 那样替换正则表达式:
使其成为 1-liner: ss = [s.replace(*r) for r in (("brown", "red"), ("lazy", "quick"))][0]
这受到任何多个 replace 方法的排序问题的影响,"abc" 和您的替换是 (("a", "b"), ("b", "a")) 您可能期望 "bac" 但您得到 "aac"。此外,每次调用都存在扫描整个字符串的性能问题,因此复杂度至少为 O(number of replacements * len(s)),加上引擎盖下发生的任何字符串模式匹配。
@MarkK 这很聪明,但在内存方面非常昂贵,因为它列出了所有中间结果的巨大列表,只是将它们全部扔给了垃圾收集器。 functools.reduce 会更尊重一点:reduce(lambda a, e: a.replace(*e), ("ab",), "abac")。无论哪种方式,我都不推荐这种方法(见上面的评论)。
B
Björn Lindqvist

这是使用 reduce 的第一个解决方案的变体,以防您喜欢功能性。 :)

repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)

martineau 的更好版本:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)

制作 repls 元组序列并取消 iteritems() 调用会更简单。即repls = ('hello', 'goodbye'), ('world', 'earth')reduce(lambda a, kv: a.replace(*kv), repls, s)。在 Python 3 中也可以保持不变。
好的!如果你使用 python3 使用 items 而不是 iteritems (现在在 dicts 东西中删除)。
@martineau:自 reduce has been removed 以来,这在 python3 中没有改变是不正确的。
@normanius:reduce 仍然存在,但是它是 Python 3 中 functools 模块(参见 docs)的一部分,所以当我说不变时,我的意思是可以运行相同的代码 - 尽管承认它如果需要,reduce 将要求已import编辑,因为它不再是内置的。
撇开语法不谈,这与 many other solutions on 此页面基本相同,后者的时间复杂度低,并且在替换过程中存在排序问题和意外行为。
m
mmj

这只是对 FJ 和 MiniQuark 出色答案的更简洁回顾,也是 bgusach 的最后但决定性的改进。实现多个同时字符串替换所需的只是以下功能:

def multiple_replace(string, rep_dict):
    pattern = re.compile("|".join([re.escape(k) for k in sorted(rep_dict,key=len,reverse=True)]), flags=re.DOTALL)
    return pattern.sub(lambda x: rep_dict[x.group(0)], string)

用法:

>>>multiple_replace("Do you like cafe? No, I prefer tea.", {'cafe':'tea', 'tea':'cafe', 'like':'prefer'})
'Do you prefer tea? No, I prefer cafe.'

如果您愿意,您可以从这个更简单的功能开始制作您自己的专用替换功能。


虽然这是一个很好的解决方案,但并发字符串替换不会给出与按顺序(链接)它们执行它们完全相同的结果——尽管这可能并不重要。
当然,对于 rep_dict = {"but": "mut", "mutton": "lamb"},字符串 "button" 会在您的代码中产生 "mutton",但如果替换被链接,则会一个接一个地给出 "lamb"
这是这段代码的主要特点,而不是缺陷。使用链式替换,它无法实现像我的示例中那样同时替换两个单词的期望行为。
如果您不需要它,它似乎不是一个很棒的功能。但是这里我们说的是同时替换,那么它确实是主要功能。使用“链式”替换,该示例的输出将是 Do you prefer cafe? No, I prefer cafe.,这根本不是我们想要的。
最佳答案。大多数其他人进行顺序而不是并发替换。
M
MiniQuark

我在 FJ 的出色回答基础上构建了这个:

import re

def multiple_replacer(*key_values):
    replace_dict = dict(key_values)
    replacement_function = lambda match: replace_dict[match.group(0)]
    pattern = re.compile("|".join([re.escape(k) for k, v in key_values]), re.M)
    return lambda string: pattern.sub(replacement_function, string)

def multiple_replace(string, *key_values):
    return multiple_replacer(*key_values)(string)

一枪使用:

>>> replacements = (u"café", u"tea"), (u"tea", u"café"), (u"like", u"love")
>>> print multiple_replace(u"Do you like café? No, I prefer tea.", *replacements)
Do you love tea? No, I prefer café.

请注意,由于仅一次完成替换,因此“café”会更改为“tea”,但不会变回“café”。

如果您需要多次执行相同的替换,您可以轻松创建替换函数:

>>> my_escaper = multiple_replacer(('"','\\"'), ('\t', '\\t'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?\tYes it does',
                       u'And can we span\nmultiple lines?\t"Yes\twe\tcan!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?\tYes it does
And can we span
multiple lines?\t\"Yes\twe\tcan!\"

改进:

把代码变成函数

添加了多行支持

修复了转义错误

易于为特定的多次替换创建函数

享受! :-)


有人可以为像我这样的 python 菜鸟一步一步地解释这个吗?
这里是蟒蛇新手,所以我将不完整地理解它.. a。将 key_values 分解为要替换的内容(由“|”连接的键)和逻辑(如果匹配是键,则返回值) b.制作一个正则表达式解析器(查找键并使用给定逻辑的“模式”) - 将其包装在 lambda 函数中并返回。我现在正在查找的东西:re.M,以及替换逻辑的 lambda 的必要性。
@Fox 你明白了。您可以定义一个函数而不是使用 lambda,这只是为了使代码更短。但请注意,pattern.sub 需要一个只有一个参数(要替换的文本)的函数,因此该函数需要访问 replace_dictre.M 允许多行替换(文档中有很好的解释:docs.python.org/2/library/re.html#re.M)。
这是一个聪明的答案,因为它通过对字符串进行单次扫描来处理重叠和交换。这个问题的许多其他答案都是陷阱……
X
Xavier Guihot

Python 3.8 开始,并引入 assignment expressions (PEP 572):= 运算符),我们可以在列表推导中应用替换:

# text = "The quick brown fox jumps over the lazy dog"
# replacements = [("brown", "red"), ("lazy", "quick")]
[text := text.replace(a, b) for a, b in replacements]
# text = 'The quick red fox jumps over the quick dog'

你知道这是否比在循环中使用替换更有效吗?我正在测试所有性能答案,但我还没有 3.8。
为什么我会在列表中获得输出?
@johnrao07 好吧,列表理解构建了一个列表。这就是为什么在这种情况下,您会得到 ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']。但是赋值表达式 (text := text.replace) 也通过改变它来迭代地构建 text 的新版本。在列表理解之后,您可以使用包含修改后文本的 text 变量。
如果要将新版本的 text 作为单行返回,还可以使用 [text := text.replace(a, b) for a, b in replacements][-1](注意 [-1]),它提取列表推导的最后一个元素;即 text 的最后一个版本。
如果您只需要最后一个元素,这将极大地浪费空间。不要将列表推导用作 reducers,尽管链接的答案不是特别有效或有用,因为它会遇到替换排序问题,就像这样。
F
Fredrik Pihl

我想建议使用字符串模板。只需将要替换的字符串放入字典中即可! docs.python.org 中的示例

>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
>>> d = dict(who='tim')
>>> Template('Give $who $100').substitute(d)
Traceback (most recent call last):
[...]
ValueError: Invalid placeholder in string: line 1, col 10
>>> Template('$who likes $what').substitute(d)
Traceback (most recent call last):
[...]
KeyError: 'what'
>>> Template('$who likes $what').safe_substitute(d)
'tim likes $what'

看起来不错,但是当添加 substitute 中未提供的键时会引发异常,因此在从用户处获取模板时要小心。
这种方法的一个缺点是模板必须包含要替换的所有且不超过所有的 $strings,请参阅 here
J
James Koss

就我而言,我需要用名称简单地替换唯一键,所以我想到了这个:

a = 'This is a test string.'
b = {'i': 'I', 's': 'S'}
for x,y in b.items():
    a = a.replace(x, y)
>>> a
'ThIS IS a teSt StrIng.'

只要您没有替换冲突,这就会起作用。如果您将 i 替换为 s,您会得到一个奇怪的行为。
如果 order 很重要,则可以使用数组而不是上面的 dict:b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) 然后,如果您小心地对数组对进行排序,则可以确保不会递归地 replace()。
似乎 dicts now maintain order,来自 Python 3.7.0。我对它进行了测试,它在我的机器上使用最新的稳定 Python 3 按顺序运行。
这与此页面上的大多数其他答案有何不同?
b
bgusach

这是我的 0.02 美元。它基于 Andrew Clark 的回答,稍微清晰一点,并且还涵盖了要替换的字符串是要替换的另一个字符串的子字符串的情况(更长的字符串获胜)

def multireplace(string, replacements):
    """
    Given a string and a replacement map, it returns the replaced string.

    :param str string: string to execute replacements on
    :param dict replacements: replacement dictionary {value to find: value to replace}
    :rtype: str

    """
    # Place longer ones first to keep shorter substrings from matching
    # where the longer ones should take place
    # For instance given the replacements {'ab': 'AB', 'abc': 'ABC'} against 
    # the string 'hey abc', it should produce 'hey ABC' and not 'hey ABc'
    substrs = sorted(replacements, key=len, reverse=True)

    # Create a big OR regex that matches any of the substrings to replace
    regexp = re.compile('|'.join(map(re.escape, substrs)))

    # For each match, look up the new string in the replacements
    return regexp.sub(lambda match: replacements[match.group(0)], string)

在此this gist中,如果您有任何建议,请随时修改。


这应该是公认的答案,因为正则表达式是由所有键构成的,方法是按长度降序对它们进行排序并将它们与 | 连接起来。正则表达式交替运算符。并且排序是必要的,以便在有任何选择的情况下选择所有可能选择中最长的一个。
我同意这是最好的解决方案,这要归功于排序。除了排序与我原来的答案相同,所以我也为我的解决方案借用了排序,以确保没有人会错过如此重要的功能。
佚名

我需要一个解决方案,其中要替换的字符串可以是正则表达式,例如通过用单个空格替换多个空格字符来帮助规范化长文本。基于其他人的一系列答案,包括 MiniQuark 和 mmj,这就是我想出的:

def multiple_replace(string, reps, re_flags = 0):
    """ Transforms string, replacing keys from re_str_dict with values.
    reps: dictionary, or list of key-value pairs (to enforce ordering;
          earlier items have higher priority).
          Keys are used as regular expressions.
    re_flags: interpretation of regular expressions, such as re.DOTALL
    """
    if isinstance(reps, dict):
        reps = reps.items()
    pattern = re.compile("|".join("(?P<_%d>%s)" % (i, re_str[0])
                                  for i, re_str in enumerate(reps)),
                         re_flags)
    return pattern.sub(lambda x: reps[int(x.lastgroup[1:])][1], string)

它适用于其他答案中给出的示例,例如:

>>> multiple_replace("(condition1) and --condition2--",
...                  {"condition1": "", "condition2": "text"})
'() and --text--'

>>> multiple_replace('hello, world', {'hello' : 'goodbye', 'world' : 'earth'})
'goodbye, earth'

>>> multiple_replace("Do you like cafe? No, I prefer tea.",
...                  {'cafe': 'tea', 'tea': 'cafe', 'like': 'prefer'})
'Do you prefer tea? No, I prefer cafe.'

对我来说主要的是你也可以使用正则表达式,例如只替换整个单词,或者规范化空格:

>>> s = "I don't want to change this name:\n  Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> multiple_replace(s, re_str_dict)
"You don't want to change this name: Philip II of Spain"

如果您想将字典键用作普通字符串,您可以在调用 multiple_replace 之前使用以下函数转义这些键:

def escape_keys(d):
    """ transform dictionary d by applying re.escape to the keys """
    return dict((re.escape(k), v) for k, v in d.items())

>>> multiple_replace(s, escape_keys(re_str_dict))
"I don't want to change this name:\n  Philip II of Spain"

以下函数可以帮助在字典键中查找错误的正则表达式(因为来自 multiple_replace 的错误消息不是很清楚):

def check_re_list(re_list):
    """ Checks if each regular expression in list is well-formed. """
    for i, e in enumerate(re_list):
        try:
            re.compile(e)
        except (TypeError, re.error):
            print("Invalid regular expression string "
                  "at position {}: '{}'".format(i, e))

>>> check_re_list(re_str_dict.keys())

请注意,它不会链接替换,而是同时执行它们。这使它更有效率,而不会限制它可以做什么。为了模仿链接的效果,您可能只需要添加更多的字符串替换对并确保对的预期顺序:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'

这很好,谢谢。是否可以改进以允许在替换中使用反向引用?我还没有立即弄清楚如何添加它。
我上面问题的答案是stackoverflow.com/questions/45630940/…
您好,我收到此脚本 TypeError: 'dict_items' object is not subscriptable 的错误。任何人都可以帮忙吗?
9
9000

注意:测试您的案例,请参阅评论。

这是一个示例,它在具有许多小替换的长字符串上更有效。

source = "Here is foo, it does moo!"

replacements = {
    'is': 'was', # replace 'is' with 'was'
    'does': 'did',
    '!': '?'
}

def replace(source, replacements):
    finder = re.compile("|".join(re.escape(k) for k in replacements.keys())) # matches every string we want replaced
    result = []
    pos = 0
    while True:
        match = finder.search(source, pos)
        if match:
            # cut off the part up until match
            result.append(source[pos : match.start()])
            # cut off the matched part and replace it in place
            result.append(replacements[source[match.start() : match.end()]])
            pos = match.end()
        else:
            # the rest after the last match
            result.append(source[pos:])
            break
    return "".join(result)

print replace(source, replacements)

关键是要避免许多长字符串的串联。我们将源字符串分割成片段,在我们形成列表时替换一些片段,然后将整个内容重新连接成一个字符串。


你有基准来支持这里的性能断言吗?
@ggorlen:实际上恰恰相反:根据我的测试,在前几千字节内的字符串上,长字符串替换和连接更快。
G
George Pipis

您可以使用支持完全匹配和正则表达式替换的 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', '\d{2}:\d{2}', '\d{2}/\d{2}/\d{4}']
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。请注意,文本上的替换是按照它们在列表中出现的顺序完成的


P
Pablo

我也在努力解决这个问题。许多替换正则表达式很困难,并且比循环 string.replace 慢四倍(在我的实验条件下)。

您绝对应该尝试使用 Flashtext 库(blog post hereGithub here)。 In my case 对于每个文档,它比 快了两个数量级,从 1.8 秒到 0.015 秒(正则表达式需要 7.7 秒)

在上面的链接中很容易找到使用示例,但这是一个工作示例:

    from flashtext import KeywordProcessor
    self.processor = KeywordProcessor(case_sensitive=False)
    for k, v in self.my_dict.items():
        self.processor.add_keyword(k, v)
    new_string = self.processor.replace_keywords(string)

请注意,Flashtext 在一次传递中进行替换(以避免 a --> b 和 b --> c 将 'a' 转换为 'c')。 Flashtext 还会查找整个单词(因此“is”不会匹配“this”)。如果您的目标是几个单词(将 'This is' 替换为 'Hello'),它可以正常工作。


如果您需要替换 HTML 标签,这将如何工作?例如,将 <p> 替换为 /n。我尝试了您的方法,但带有标签 flashtext 似乎无法解析它?
我不确定为什么它没有按您的预期工作。一种可能性是这些标签没有用空格分隔,记住 Flashtext 会查找整个单词。解决此问题的一种方法是首先使用简单的替换,以便“Hi

there”变为“Hi

there”。完成后,您需要小心删除不需要的空格(也是简单的替换?)。希望有帮助。

谢谢,您可以设置 <> 来标记单词的结尾(但要包含在替换中)?
我相信“单词”只用空格标记。也许您可以在“KeywordProcessor”中设置一些可选参数。否则考虑上面的方法:用“<”替换“<”,应用Flashtext然后替换回来(例如,在你的情况下,“<”到“<”和“\n”到“\n”可能有效)。
感谢您提及这个项目。它完美地解决了我的几个要求。
m
mcsoini

我觉得这个问题需要一个单行递归 lambda 函数来回答完整性,只是因为。所以那里:

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.popitem()), d)

用法:

>>> mrep('abcabc', {'a': '1', 'c': '2'})
'1b21b2'

笔记:

这会消耗输入字典。

Python dicts 自 3.6 起保留键顺序;其他答案中的相应警告不再相关。为了向后兼容,可以使用基于元组的版本:

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

注意:与python中的所有递归函数一样,太大的递归深度(即太大的替换字典)会导致错误。参见例如 here


使用大字典时遇到 RecursionError !
@Pablo 有趣。多大?请注意,所有递归函数都会发生这种情况。请参阅此处的示例:stackoverflow.com/questions/3323001/…
我的替换字典接近 10 万个术语……到目前为止,使用 string.replace 是最好的方法。
@Pablo 在这种情况下你不能使用递归函数。通常,sys.getrecursionlimit() 是一对 1000,最大值。使用循环或类似的东西,或尝试简化替换。
是的,恐怕这里真的没有捷径。
T
Tanvir Ahmed

我今天遇到了类似的问题,我不得不多次使用 .replace() 方法,但对我来说感觉不太好。所以我做了这样的事情:

REPLACEMENTS = {'<': '&lt;', '>': '&gt;', '&': '&amp;'}

event_title = ''.join([REPLACEMENTS.get(c,c) for c in event['summary']])

M
Miroslav Kašpar

我在学校的一项作业中做了类似的练习。这是我的解决方案

dictionary = {1: ['hate', 'love'],
              2: ['salad', 'burger'],
              3: ['vegetables', 'pizza']}

def normalize(text):
    for i in dictionary:
        text = text.replace(dictionary[i][0], dictionary[i][1])
    return text

在测试字符串上自己查看结果

string_to_change = 'I hate salad and vegetables'
print(normalize(string_to_change))

i
inspectorG4dget

你真的不应该这样做,但我觉得它太酷了:

>>> replacements = {'cond1':'text1', 'cond2':'text2'}
>>> cmd = 'answer = s'
>>> for k,v in replacements.iteritems():
>>>     cmd += ".replace(%s, %s)" %(k,v)
>>> exec(cmd)

现在,answer 依次是所有替换的结果

同样,这非常hacky,不应该经常使用。但是很高兴知道如果您需要,您可以做这样的事情。


C
Carson

对于只替换一个字符,使用 translatestr.maketrans 是我最喜欢的方法。

tl;博士> result_string = your_string.translate(str.maketrans(dict_mapping))

演示

my_string = 'This is a test string.'
dict_mapping = {'i': 's', 's': 'S'}
result_good = my_string.translate(str.maketrans(dict_mapping))
result_bad = my_string
for x, y in dict_mapping.items():
    result_bad = result_bad.replace(x, y)
print(result_good)  # ThsS sS a teSt Strsng.
print(result_bad)   # ThSS SS a teSt StrSng.

我也喜欢maketrans/translate!不幸的是,它对单词替换没有用,因为它只能替换单个字符
d
del_hol

我不知道速度,但这是我的工作日快速修复:

reduce(lambda a, b: a.replace(*b)
    , [('o','W'), ('t','X')] #iterable of pairs: (oldval, newval)
    , 'tomato' #The string from which to replace values
    )

...但我喜欢上面的#1 regex 答案。注意 - 如果一个新值是另一个新值的子字符串,则该操作不是可交换的。


T
Tommy Sandi

从 Andrew 的宝贵答案开始,我开发了一个脚本,该脚本从文件中加载字典并详细说明打开的文件夹中的所有文件以进行替换。该脚本从您可以设置分隔符的外部文件加载映射。我是一个初学者,但我发现这个脚本在多个文件中进行多次替换时非常有用。它在几秒钟内加载了一个包含 1000 多个条目的字典。它并不优雅,但对我有用

import glob
import re

mapfile = input("Enter map file name with extension eg. codifica.txt: ")
sep = input("Enter map file column separator eg. |: ")
mask = input("Enter search mask with extension eg. 2010*txt for all files to be processed: ")
suff = input("Enter suffix with extension eg. _NEW.txt for newly generated files: ")

rep = {} # creation of empy dictionary

with open(mapfile) as temprep: # loading of definitions in the dictionary using input file, separator is prompted
    for line in temprep:
        (key, val) = line.strip('\n').split(sep)
        rep[key] = val

for filename in glob.iglob(mask): # recursion on all the files with the mask prompted

    with open (filename, "r") as textfile: # load each file in the variable text
        text = textfile.read()

        # start replacement
        #rep = dict((re.escape(k), v) for k, v in rep.items()) commented to enable the use in the mapping of re reserved characters
        pattern = re.compile("|".join(rep.keys()))
        text = pattern.sub(lambda m: rep[m.group(0)], text)

        #write of te output files with the prompted suffice
        target = open(filename[:-4]+"_NEW.txt", "w")
        target.write(text)
        target.close()

e
emorjon2

这是我解决问题的方法。我在聊天机器人中使用它来一次替换不同的单词。

def mass_replace(text, dct):
    new_string = ""
    old_string = text
    while len(old_string) > 0:
        s = ""
        sk = ""
        for k in dct.keys():
            if old_string.startswith(k):
                s = dct[k]
                sk = k
        if s:
            new_string+=s
            old_string = old_string[len(sk):]
        else:
            new_string+=old_string[0]
            old_string = old_string[1:]
    return new_string

print mass_replace("The dog hunts the cat", {"dog":"cat", "cat":"dog"})

这将变为 The cat hunts the dog


A
Akhil Thayyil

另一个例子:输入列表

error_list = ['[br]', '[ex]', 'Something']
words = ['how', 'much[ex]', 'is[br]', 'the', 'fish[br]', 'noSomething', 'really']

所需的输出将是

words = ['how', 'much', 'is', 'the', 'fish', 'no', 'really']

代码 :

[n[0][0] if len(n[0]) else n[1] for n in [[[w.replace(e,"") for e in error_list if e in w],w] for w in words]] 

i
information_interchange

我的方法是首先标记字符串,然后为每个标记决定是否包含它。

如果我们可以假设 O(1) 查找 hashmap/set,可能会更高效:

remove_words = {"we", "this"}
target_sent = "we should modify this string"
target_sent_words = target_sent.split()
filtered_sent = " ".join(list(filter(lambda word: word not in remove_words, target_sent_words)))

filtered_sent 现在是 'should modify string'


m
mnesarco

这是一个支持基本正则表达式替换的版本。主要限制是表达式不能包含子组,并且可能存在一些边缘情况:

基于@bgusach 等的代码

import re

class StringReplacer:

    def __init__(self, replacements, ignore_case=False):
        patterns = sorted(replacements, key=len, reverse=True)
        self.replacements = [replacements[k] for k in patterns]
        re_mode = re.IGNORECASE if ignore_case else 0
        self.pattern = re.compile('|'.join(("({})".format(p) for p in patterns)), re_mode)
        def tr(matcher):
            index = next((index for index,value in enumerate(matcher.groups()) if value), None)
            return self.replacements[index]
        self.tr = tr

    def __call__(self, string):
        return self.pattern.sub(self.tr, string)

测试

table = {
    "aaa"    : "[This is three a]",
    "b+"     : "[This is one or more b]",
    r"<\w+>" : "[This is a tag]"
}

replacer = StringReplacer(table, True)

sample1 = "whatever bb, aaa, <star> BBB <end>"

print(replacer(sample1))

# output: 
# whatever [This is one or more b], [This is three a], [This is a tag] [This is one or more b] [This is a tag]

诀窍是通过位置来识别匹配组。它不是超级高效(O(n)),但它有效。

index = next((index for index,value in enumerate(matcher.groups()) if value), None)

更换一次完成。


B
Brandon H

或者只是为了快速破解:

for line in to_read:
    read_buffer = line              
    stripped_buffer1 = read_buffer.replace("term1", " ")
    stripped_buffer2 = stripped_buffer1.replace("term2", " ")
    write_to_file = to_write.write(stripped_buffer2)

S
Stefan Gruenwald

这是使用字典的另一种方法:

listA="The cat jumped over the house".split()
modify = {word:word for number,word in enumerate(listA)}
modify["cat"],modify["jumped"]="dog","walked"
print " ".join(modify[x] for x in listA)

佚名
sentence='its some sentence with a something text'

def replaceAll(f,Array1,Array2):
    if len(Array1)==len(Array2):
        for x in range(len(Array1)):
            return f.replace(Array1[x],Array2[x])

newSentence=replaceAll(sentence,['a','sentence','something'],['another','sentence','something something'])

print(newSentence)

单一方法不可能多次返回