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

2024-11-25 08:49:00
admin
原创
259
摘要:问题描述:我想使用 .replace 函数来替换多个字符串。我目前有string.replace("condition1", "") 但想要类似的东西string.replace("condition1", "").replace...

问题描述:

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

我目前有

string.replace("condition1", "")

但想要类似的东西

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

尽管这听起来不像是一个好的语法

正确的做法是什么?有点像在 grep/regex 中,你可以将字段替换为某些搜索字符串


解决方案 1:

这是一个使用正则表达式来实现这个效果的简短例子:

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.items()) 
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--'

解决方案 2:

您可以制作一个很好的小循环函数。

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 版本,因为标准字典被更改为使用插入顺序进行迭代。

例如:

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:

“这是我的猪,这是我的猪。”

可能的输出 #2

“这是我的狗,这是我的猪。”

一个可能的解决方法是使用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字符串太大或者字典中有很多对,则效率低下。

解决方案 3:

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

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

解决方案 4:

如果您喜欢功能性,这是使用reduce(从 导入)的第一个解决方案的变体。:)functools

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)

解决方案 5:

这只是 FJ 和 MiniQuark 精彩答案的更简洁回顾,以及 bgusach 的最后但决定性的改进。实现多个同时字符串替换所需的只是以下函数:

import re
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.'

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

解决方案 6:

从开始,以及赋值表达式(PEP 572)Python 3.8的引入(运算符),我们可以在列表推导中应用替换::=

# 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'

解决方案 7:

我根据 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'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?    Yes it does',
                       u'And can we span
multiple lines?    "Yes    we    can!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?    Yes it does
And can we span
multiple lines?    \"Yes    we    can!\"

改进:

  • 将代码变成函数

  • 增加了多行支持

  • 修复了转义中的一个错误

  • 轻松创建用于特定多重替换的函数

享受! :-)

解决方案 8:

我想建议使用字符串模板。只需将要替换的字符串放在字典中,一切就都搞定了!示例来自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'

解决方案 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)

这就是这个要点,如果您有任何建议,请随意修改它。

解决方案 10:

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

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.'

解决方案 11:

我需要一个解决方案,其中要替换的字符串可以是正则表达式,例如通过将多个空格字符替换为一个空格字符来帮助规范化长文本。基于其他人(包括 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:
  Philip II of Spain"
>>> re_str_dict = {r'I': 'You', r'[
     ]+': ' '}
>>> 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:
  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'

解决方案 12:

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

这是一个示例,它对于包含许多小替换的长字符串来说效率更高。

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)

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

解决方案 13:

您可以使用支持精确匹配和正则表达式替换的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

您可以在此处找到示例。请注意,文本的替换是按照它们在列表中出现的顺序进行的

解决方案 14:

我也一直在努力解决这个问题。正则表达式在进行多次替换时会遇到困难,并且比循环慢大约四倍string.replace(在我的实验条件下)。

您绝对应该尝试使用Flashtext库(博客文章在这里,Github 在这里)。就我而言,它快了两个数量级,每个文档从 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 --> bb --> c将“a”转换为“c”)。Flashtext 还会查找整个单词(因此“is”不会匹配“th is ”)。如果您的目标是几个单词(将“This is”替换为“Hello”),它会正常工作。

解决方案 15:

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

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))

解决方案 16:

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

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

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

解决方案 17:

我觉得这个问题需要一个单行递归 lambda 函数答案才能完整,仅此而已。所以:

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

用法:

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

笔记:

  • 这会消耗输入字典。

  • 从 3.6 开始,Python 字典保留键顺序;其他答案中的相应警告不再相关。为了向后兼容,可以采用基于元组的版本:

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

注意:与 Python 中的所有递归函数一样,递归深度过大(即替换字典过大)将导致错误。例如,参见此处。

解决方案 18:

你真的不应该这样做,但我发现这太酷了:

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

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

再次强调,这是非常不成熟的,不应该经常使用。但如果你需要的话,知道你可以做这样的事情,这很好。

解决方案 19:

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

总结 >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.

解决方案 20:

我不知道速度如何,但这是我日常工作的快速解决方法:

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 正则表达式答案。注意 - 如果一个新值是另一个值的子字符串,则该操作不可交换。

解决方案 21:

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

代码基于@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)

更换一次性完成。

解决方案 22:

从 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('
').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()

解决方案 23:

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

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

解决方案 24:

另一个例子:输入列表

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]] 

解决方案 25:

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

如果我们可以假设哈希图/集合的查找时间为 O(1),那么性能可能会更高:

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'

解决方案 26:

我知道这已经很老了,但我一直在尝试将 json 转换为 PHP,并且我喜欢使用括号和新行来查看每个替换将执行什么操作。

这是代码。

def do_syntax_changes(jsonInS):   
    """ generated by Joe
    see [How to replace multiple substrings of a string?](https://stackoverflow.com/q/6116978/601770)
        also see [How to replace multiple substrings of a string? >> Py3.8 assignment expressions](https://stackoverflow.com/a/55889140/601770)
    """
    
    phpOutS = ''
    for lineI in jsonInS:
        oS = (((((
        lineI.replace('null', 'NULL')
        ).replace('true', 'TRUE')
        ).replace('false', 'FALSE')
        ).replace(':', '=>')
        ).replace('{', '[')
        ).replace('}', ']') 
        phpOutS += oS
    return phpOutS

:)

它似乎也能生成良好的 PHP。

解决方案 27:

或者只是为了快速破解:

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)

解决方案 28:

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

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)

解决方案 29:

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)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3838  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2707  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   29  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   23  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   26  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用