【问题标题】:Python replace function [replace once]Python替换函数[替换一次]
【发布时间】:2013-02-25 18:47:12
【问题描述】:

我在使用 Python 编写的程序方面需要帮助。

假设我想将单词 "steak" 的每个实例替换为 "ghost"(随它去......)但我也想同时将单词 "ghost" 的每个实例替换为 "steak"时间。以下代码不起作用:

 s="The scary ghost ordered an expensive steak"
 print s
 s=s.replace("steak","ghost")
 s=s.replace("ghost","steak")
 print s

打印:The scary steak ordered an expensive steak

我想要得到的是The scary steak ordered an expensive ghost

【问题讨论】:

  • 你想让unghosted变成unsteaked吗? (前一周从一个问题中想到的例子是“名字”和“珐琅”。)
  • 嗯,但也许您希望将ghosts 转换为steaks,反之亦然。这就是.replace 会做的事情。
  • 您只想替换一次吗?还是每个实例?你应该澄清你的问题的那部分......(特别是考虑到它得到的关注)

标签: python string replace


【解决方案1】:

将其中一个单词重命名为文本中未出现的临时值。请注意,对于非常大的文本,这不是最有效的方法。为此,re.sub 可能更合适。

 s="The scary ghost ordered an expensive steak"
 print s
 s=s.replace("steak","temp")
 s=s.replace("ghost","steak")
 S=s.replace("temp","steak")
 print s

【讨论】:

  • 但是说我想要一个用户输入,有没有办法确保他们不使用临时值而不使临时值像“zxdasd”这样的随机字母序列?跨度>
  • @user2154113 您可以先检查该值是否在字符串中。无论如何,这是错误的做法。正确的方法是 Leif Andersen 的。
  • 如果有多个实例,Leif 的答案如何替换每个实例?这种方式确实有效。
  • 这可行,但需要临时值。 (你可以使用一些随机的 Unicode 来降低风险,但这不是一个非常通用的解决方案)。
  • @MarkTolonen:是的,但通常你希望 temp 是字符串中不可能存在的东西(即它应该是带外的)。
【解决方案2】:

string.replace() 方法中使用count 变量。因此,使用您的代码,您将拥有:

s="The scary ghost ordered an expensive steak"
print s
s=s.replace("steak","ghost", 1)
s=s.replace("ghost","steak", 1)
print s

http://docs.python.org/2/library/stdtypes.html

【讨论】:

  • 这可行,但仅适用于这个特定示例。如果单词的实例更多,或者即使单词的顺序不同,它也不会起作用。
  • 没错,但由于我们没有得到任何其他上下文来解释为什么要更改这些词,所以这是最好的方法。但是,是的,如果他确实想要一个更强大的解决方案,那么这是行不通的。
  • +1 是唯一的“替换一次”。所有其他答案都解决了在字符串中交换 X,Y 的一般情况,但您的答案回答了这个问题。
  • @RyanAmos --“替换一次”在标题中,但如果您阅读问题的正文,它会显示“每个实例”...
【解决方案3】:

这样的事情怎么样?将原件存储在拆分列表中,然后有一个翻译字典。保持核心代码简短,然后在需要调整翻译时调整字典。另外,易于移植到函数:

 def translate_line(s, translation_dict):
    line = []
    for i in s.split():
       # To take account for punctuation, strip all non-alnum from the
       # word before looking up the translation.
       i = ''.join(ch for ch in i if ch.isalnum()]
       line.append(translation_dict.get(i, i))
    return ' '.join(line)


 >>> translate_line("The scary ghost ordered an expensive steak", {'steak': 'ghost', 'ghost': 'steak'})
 'The scary steak ordered an expensive ghost'

【讨论】:

  • 这个想法+1,但你还没有完全做到。这仅适用于没有标点符号的情况。
  • @TimPietzcker 是的,绝对不是一个可靠的解决方案 :) 您可能会从单词中删除所有非字母数字字符,然后将其用于翻译.. 我会调整
【解决方案4】:

我可能会在这里使用正则表达式:

>>> import re
>>> s = "The scary ghost ordered an expensive steak"
>>> sub_dict = {'ghost':'steak','steak':'ghost'}
>>> regex = '|'.join(sub_dict)
>>> re.sub(regex, lambda m: sub_dict[m.group()], s)
'The scary steak ordered an expensive ghost'

或者,作为可以复制/粘贴的功能:

import re
def word_replace(replace_dict,s):
    regex = '|'.join(replace_dict)
    return re.sub(regex, lambda m: replace_dict[m.group()], s)

基本上,我创建了一个我想用其他词替换的词的映射 (sub_dict)。我可以从该映射创建一个正则表达式。在这种情况下,正则表达式为"steak|ghost"(或"ghost|steak"——顺序无关紧要),而正则表达式引擎完成了查找非重叠序列并相应地替换它们的其余工作。


一些可能有用的修改

  • regex = '|'.join(map(re.escape,replace_dict)) -- 允许正则表达式在其中包含特殊的正则表达式语法(如括号)。这会转义特殊字符以使正则表达式与文字文本匹配。
  • regex = '|'.join(r'\b{0}\b'.format(x) for x in replace_dict) -- 如果我们的一个词是另一个词的子字符串,请确保我们不匹配。换句话说,将he 更改为she,但不要将the 更改为tshe

【讨论】:

  • 使用正则表达式的一个优点是您可以添加边界标记来帮助防止意外的子字符串匹配。 [当然,你也可以在没有正则表达式的情况下完成这项工作。]
  • @DSM -- 当然 -- 实际上这只是一个修改你如何加入sub_dict 的问题。类似:'|'.join(r'(?:\b{}\b)'.format(x) for x in regex)——如果我正确地记住了我的正则表达式语法:)
  • @nneonneo -- 对。更新了我的评论。谢谢:)
  • (哦,关闭引用。:P)
  • 转义字符串的好主意,这样当有人添加具有特殊含义的字符串时它不会中断regex = '|'.join(re.escape(k) for k in sub_dict)
【解决方案5】:

将字符串按其中一个目标拆分,进行替换,然后将整个内容重新组合在一起。

pieces = s.split('steak')
s = 'ghost'.join(piece.replace('ghost', 'steak') for piece in pieces)

完全.replace() 那样工作,包括忽略单词边界。所以它会将"steak ghosts" 变成"ghost steaks"

【讨论】:

  • +1:这比正则表达式解决方案具有更好的性能。查看我已删除的答案
  • @Abhijit -- 很有趣,但并不奇怪这会比 re.sub 快。我仍然喜欢 re.sub ......我发现它更加明确和灵活(例如添加单词边界)。请注意,如果您真的希望它更快,您可以将 join 中的内容放在 list-comp 而不是生成器表达式中。对于.join 来说,这总是更快……从风格上看,它更丑。
  • @nneonneo -- 我想我应该在''.join 上阅读python3 的源代码。在 python2 上,生成器无论如何都会变成一个列表,这就是它总是更快的原因。但也许他们在 python3.x 上做了一些不同的事情?
  • @mgilson:哎呀。误读 timeit 结果。我想 DST 真的把我搞砸了。 .join 如果给定一个genexpr,总是比给定一个列表慢10~15%。
  • @nneonneo -- 你让我在那里循环了一分钟:)。 join 需要一个列表,以便它可以遍历它两次。第一次迭代计算出输出字符串的大小,以便分配足够的内存。
【解决方案6】:

注意考虑到这个问题的收视率,我针对不同类型的测试用例取消删除并重写了它

我从答案中考虑了四个相互竞争的实现

>>> def sub_noregex(hay):
    """
    The Join and replace routine which outpeforms the regex implementation. This
    version uses generator expression
    """
    return 'steak'.join(e.replace('steak','ghost') for e in hay.split('ghost'))

>>> def sub_regex(hay):
    """
    This is a straight forward regex implementation as suggested by @mgilson
    Note, so that the overheads doesn't add to the cummulative sum, I have placed
    the regex creation routine outside the function
    """
    return re.sub(regex,lambda m:sub_dict[m.group()],hay)

>>> def sub_temp(hay, _uuid = str(uuid4())):
    """
    Similar to Mark Tolonen's implementation but rather used uuid for the temporary string
    value to reduce collission
    """
    hay = hay.replace("steak",_uuid).replace("ghost","steak").replace(_uuid,"steak")
    return hay

>>> def sub_noregex_LC(hay):
    """
    The Join and replace routine which outpeforms the regex implementation. This
    version uses List Comprehension
    """
    return 'steak'.join([e.replace('steak','ghost') for e in hay.split('ghost')])

一个广义的timeit函数

>>> def compare(n, hay):
    foo = {"sub_regex": "re",
           "sub_noregex":"",
           "sub_noregex_LC":"",
           "sub_temp":"",
           }
    stmt = "{}(hay)"
    setup = "from __main__ import hay,"
    for k, v in foo.items():
        t = Timer(stmt = stmt.format(k), setup = setup+ ','.join([k, v] if v else [k]))
        yield t.timeit(n)

以及通用测试例程

>>> def test(*args, **kwargs):
    n = kwargs['repeat']
    print "{:50}{:^15}{:^15}{:^15}{:^15}".format("Test Case", "sub_temp",
                             "sub_noregex ", "sub_regex",
                             "sub_noregex_LC ")
    for hay in args:
        hay, hay_str = hay
        print "{:50}{:15.10}{:15.10}{:15.10}{:15.10}".format(hay_str, *compare(n, hay))

测试结果如下

>>> test((' '.join(['steak', 'ghost']*1000), "Multiple repeatation of search key"),
         ('garbage '*998 + 'steak ghost', "Single repeatation of search key at the end"),
         ('steak ' + 'garbage '*998 + 'ghost', "Single repeatation of at either end"),
         ("The scary ghost ordered an expensive steak", "Single repeatation for smaller string"),
         repeat = 100000)
Test Case                                            sub_temp     sub_noregex      sub_regex   sub_noregex_LC 
Multiple repeatation of search key                   0.2022748797   0.3517142003   0.4518992298   0.1812594258
Single repeatation of search key at the end          0.2026047957   0.3508259952   0.4399926194   0.1915298898
Single repeatation of at either end                  0.1877455356   0.3561734007   0.4228843986   0.2164233388
Single repeatation for smaller string                0.2061019057   0.3145984487   0.4252060592   0.1989413449
>>> 

根据测试结果

  1. Non Regex LC 和 temp 变量替换具有更好的性能,但 temp 变量的使用性能并不一致

  2. LC 版本的性能优于生成器(已确认)

  3. 正则表达式的速度要慢两倍以上(因此,如果这段代码是瓶颈,则可以重新考虑实现更改)

  4. Regex 和非 regex 版本同样健壮并且可以扩展

【讨论】:

  • 嗯。我不得不承认我对此感到困惑。所有非正则表达式版本都执行更多的内存分配。这应该是限制性能的步骤。您是否尝试过预编译正则表达式?你知道 Python 使用的是哪个正则表达式引擎吗? (我也很震惊,列表解析比生成器表达式(这么多!)快。看起来是 CPython 的一个严重弱点。)
猜你喜欢
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-19
  • 2022-10-02
相关资源
最近更新 更多