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

CQM*_*CQM 247 python text replace

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

我现在有

string.replace("condition1", "")
Run Code Online (Sandbox Code Playgroud)

但是想要有类似的东西

string.replace("condition1", "").replace("condition2", "text")
Run Code Online (Sandbox Code Playgroud)

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

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

And*_*ark 242

这是一个简短的例子,应该使用正则表达式:

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)
Run Code Online (Sandbox Code Playgroud)

例如:

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

  • dkamins:它不是太聪明,它甚至不如它应该的那么聪明(我们应该在使用"|"加入它们之前使用regex-escape键).为什么不是过度工程?因为这样我们一次性完成(=快速),我们同时做所有的替换,避免像"垃圾邮件"这样的冲突.replace("垃圾邮件","鸡蛋").替换("沙" ,"md5")```eggmd5m md5"`而不是`"eggsham md5"` (24认同)
  • 对于python 3,使用items()而不是iteritems(). (12认同)
  • 您好,我创建了一个小小的要点,其中包含更清晰的片段版本.它应该稍微更高效:https://gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729 (11认同)
  • @AndrewClark如果你能用lambda解释最后一行发生的事情,我将不胜感激. (8认同)
  • 替换发生在一次通过. (6认同)
  • 我认为它很整洁。虽然我会把它包装在一个函数中。 (2认同)
  • @AndrewClark这个答案可能应该指定它是用于Python2还是Python3,因为使用字典的方式已经改变。 (2认同)
  • @minerals lambda是一个匿名函数.在那里,它需要一个值(`m`)并返回以下表达式的结果.或者,您可以创建一个命名函数`def replace_conditions(text,rep):return rep [re.escape(text.group(0))]`,为您的文本`text ="(condition1)指定一个变量,并且 - condition2 - "`并使用该函数和原始文本生成的模式调用?`sub`:`pattern.sub(replace_conditions(text,rep),text)`. (2认同)

Jos*_*sen 117

你可以做一个很好的小循环功能.

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text
Run Code Online (Sandbox Code Playgroud)

text完整字符串在哪里,dic是一个字典 - 每个定义都是一个字符串,它将替换该术语的匹配.

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


小心: Python字典没有可靠的迭代顺序.此解决方案仅解决您的问题,如果:

  • 替换顺序无关紧要
  • 替换可以改变以前替换的结果

例如:

d = { "cat": "dog", "dog": "pig"}
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, d)
print(mySentence)
Run Code Online (Sandbox Code Playgroud)

可能的输出#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")])
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, od)
print(mySentence)
Run Code Online (Sandbox Code Playgroud)

输出:

"This is my pig and this is my pig."
Run Code Online (Sandbox Code Playgroud)

小心#2:如果你的text字符串太大或字典中有很多对,效率低下.

  • 应用不同替换的顺序很重要 - 所以不要使用标准的dict,而应考虑使用`OrderedDict` - 或2元组列表. (37认同)
  • 性能方面,它比Valentin所说的更糟糕 - 它会遍历文本的次数与dic中的项目一样多!如果'text'很小但很好,对于大文本来说很糟糕. (5认同)
  • 请注意,这可能会产生意外结果,因为第一次迭代中新插入的文本可以在第二次迭代中匹配.例如,如果我们天真地尝试将所有'A'替换​​为'B'而将所有'B'替换为'C',则字符串'AB'将被转换为'CC'而不是'BC'. (5认同)
  • 这使得迭代字符串两次......不利于表演. (4认同)
  • 注意:从 **Python 3.7** 开始,“dict 对象的插入顺序保留性质已被声明为 Python 语言规范的正式部分。” -- [3.7 发行说明](https://docs.python.org/3/whatsnew/3.7.html) (3认同)
  • 对于某些情况,这是一个很好的解决方案 例如,我只想要2个字符,我不关心它们进入的顺序,因为替换键与任何值都不匹配.但我确实希望清楚发生了什么. (2认同)

Bjö*_*ist 88

以下是使用reduce的第一个解决方案的变体,以备您正常使用.:)

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

martineau甚至更好的版本:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)
Run Code Online (Sandbox Code Playgroud)

  • 使`repl`成为一系列元组并取消`iteritems()`调用会更简单.即`repls =('hello','goodbye'),('world','earth')`和`reduce(lambda a,kv:a.replace(*kv),repls,s)`.在Python 3中也可以保持不变. (7认同)
  • @normanius:`reduce`仍然存在,但它是`functools`模块的一部分(参见[docs](https://docs.python.org/3.0/library/functools.html#functools.reduce) )在Python 3中,所以当我说没有改变时,我的意思是可以运行相同的代码 - 尽管如此,它必须要求`reduce`在必要时被导入,因为它不再是内置的. (3认同)
  • @martineau:自从 [`reduce` 已被删除](/sf/answers/954727231/) 以来,这在 python3 中没有改变是不正确的。 (2认同)

小智 85

为什么不这样的解决方案呢?

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
Run Code Online (Sandbox Code Playgroud)

  • 这会受到任何多个“替换”方法的排序问题,“abc”,并且您的替换是您可能期望的“((“a”,“b”),(“b”,“a”))` “bac”,但你得到“aac”。此外,每次调用都会扫描整个字符串,这会带来性能问题,因此复杂度至少为“O(替换次数 * len(s))”,再加上幕后发生的任何字符串模式匹配。 (4认同)
  • 这非常有用、简单且便携。 (2认同)
  • 使其成为1行: ss = [s.replace(*r) for r in (("brown", "red"), ("lazy", "quick"))][0] (2认同)
  • @MarkK,这很聪明,但在内存方面非常昂贵,因为它生成了所有中间结果的巨大列表,只是将其全部扔给了垃圾收集器。`functools.reduce` 会更尊重一点:`reduce(lambda a, e: a.replace(*e), ("ab",), "abac")`。不管怎样,我从根本上不推荐这种方法(见上面的评论)。 (2认同)

mmj*_*mmj 33

这只是对FJ和MiniQuark的一个更简洁的回顾.您需要实现多个同时串替换的功能如下:

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)
Run Code Online (Sandbox Code Playgroud)

用法:

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

如果您愿意,您可以从这个更简单的更换功能开始.

  • 当然,使用`rep_dict = {"but": "mut", "mutton": "lamb"}` 字符串`"button"` 会在你的代码中产生`"mutton"`,但会给出`"lamb"`如果替换被链接,一个接一个。 (2认同)
  • 这是此代码的主要特征,而不是缺陷.对于链式替换,它无法实现在我的示例中同时和相互替换两个单词的期望行为. (2认同)

Min*_*ark 28

我根据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)
Run Code Online (Sandbox Code Playgroud)

一次性用法:

>>> 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é.
Run Code Online (Sandbox Code Playgroud)

请注意,由于替换仅在一次通过中完成,"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!\"
Run Code Online (Sandbox Code Playgroud)

改进:

  • 将代码转换为函数
  • 增加了多线支持
  • 修复了转义中的错误
  • 易于为特定的多次替换创建功能

请享用!:-)


Fre*_*ihl 20

我想提出字符串模板的用法.只需将要替换的字符串放在字典中即可完成所有设置!来自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'
Run Code Online (Sandbox Code Playgroud)

  • 这种方法的一个缺点是模板必须包含所有,并且不超过所有要替换的$字符串,请参见[here](http://stackoverflow.com/questions/12768107/string-substitutions-using-templates-在-蟒蛇) (2认同)

Jam*_*oss 14

在我的情况下,我需要用名称简单替换唯一键,所以我想到了这一点:

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.'
Run Code Online (Sandbox Code Playgroud)

  • 只要您没有替换冲突,此方法就起作用。如果将i替换为s,则会得到怪异的行为。 (2认同)

Xav*_*hot 10

从开始Python 3.8,并引入赋值表达式(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'
Run Code Online (Sandbox Code Playgroud)

  • 如果您只需要最后一个元素,这会浪费大量空间。不要使用列表推导式作为[reducers](/sf/answers/663598071/),尽管链接的答案不是特别有效或有用,因为它遇到了替换排序问题,就像这样。 (3认同)
  • 为什么我会得到列表中的输出? (2认同)

bgu*_*ach 9

我的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)
Run Code Online (Sandbox Code Playgroud)

正是在这个要点中,如果您有任何建议,请随时修改它.

  • 这应该是公认的答案,因为正则表达式是通过按长度降序对所有键进行排序并将它们与 | 连接起来而构建的。正则表达式交替运算符。并且排序是必要的,以便在有任何替代方案时选择所有可能选择中最长的。 (2认同)

小智 7

我需要一个解决方案,其中要替换的字符串可以是正则表达式,例如,通过用单个字符替换多个空白字符来帮助规范化长文本.基于其他人的一系列答案,包括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)
Run Code Online (Sandbox Code Playgroud)

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

>>> 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.'
Run Code Online (Sandbox Code Playgroud)

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

>>> 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"
Run Code Online (Sandbox Code Playgroud)

如果要将字典键用作普通字符串,则可以在使用例如此函数调用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"
Run Code Online (Sandbox Code Playgroud)

以下函数可以帮助您在字典键中查找错误的正则表达式(因为来自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())
Run Code Online (Sandbox Code Playgroud)

请注意,它不会链接替换,而是同时执行它们.这样可以在不限制其功能的情况下提高效率.要模仿链接的效果,您可能只需要添加更多字符串替换对并确保对的预期排序:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'
Run Code Online (Sandbox Code Playgroud)


900*_*000 6

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

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

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)
Run Code Online (Sandbox Code Playgroud)

重点是避免长字符串的多次串联。我们将源字符串切成片段,在形成列表时替换一些片段,然后将整个字符串重新连接回字符串。


小智 5

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

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
Run Code Online (Sandbox Code Playgroud)

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

string_to_change = 'I hate salad and vegetables'
print(normalize(string_to_change))
Run Code Online (Sandbox Code Playgroud)