9

php具有以下strtr 功能:

strtr('aa-bb-cc', array('aa' => 'bbz', 'bb' => 'x', 'cc' => 'y'));
# bbz-x-y

它用相应的值替换字符串中的字典键,并且(重要)不替换已替换的字符串。天真的尝试在 python 中编写相同的内容:

def strtr(strng, replace):
    for s, r in replace.items():
        strng = strng.replace(s, r)
    return strng

strtr('aa-bb-cc', {'aa': 'bbz', 'bb': 'x', 'cc': 'y'})

返回xz-x-y不是我们想要的(bb再次被替换)。如何更改上述函数,使其行为与 php 对应?

(如果可能的话,我更喜欢没有正则表达式的答案)。

更新:这里有一些很好的答案。我对它们进行了计时,发现对于短字符串,Gumbo 的版本似乎是最快的,在较长的字符串上,获胜者是re解决方案:

# 'aa-bb-cc'
0.0258 strtr_thg
0.0274 strtr_gumbo
0.0447 strtr_kojiro
0.0701 strtr_aix

# 'aa-bb-cc'*10
0.1474 strtr_aix
0.2261 strtr_thg
0.2366 strtr_gumbo
0.3226 strtr_kojiro

我自己的版本(稍微优化了 Gumbo 的):

def strtr(strng, replace):
    buf, i = [], 0
    while i < len(strng):
        for s, r in replace.items():
            if strng[i:len(s)+i] == s:
                buf.append(r)
                i += len(s)
                break
        else:
            buf.append(strng[i])
            i += 1
    return ''.join(buf)

完整的代码和时间:https ://gist.github.com/2889181

4

5 回答 5

7

以下使用正则表达式来做到这一点:

import re

def strtr(s, repl):
  pattern = '|'.join(map(re.escape, sorted(repl, key=len, reverse=True)))
  return re.sub(pattern, lambda m: repl[m.group()], s)

print(strtr('aa-bb-cc', {'aa': 'bbz', 'bb': 'x', 'cc': 'y'}))

与 PHP 的版本一样,它优先选择更长的匹配项。

于 2012-06-07T12:40:10.063 回答
5
def strtr(strng, replace):
    if replace and strng:
        s, r = replace.popitem()
        return r.join(strtr(subs, dict(replace)) for subs in strng.split(s))
    return strng

j=strtr('aa-bb-cc', {'aa': 'bbz', 'bb': 'x', 'cc': 'y'})
assert j=='bbz-x-y', j
于 2012-06-07T12:51:38.343 回答
3

这是一个简单的算法:

使用索引逐个字符地遍历原始字符串,并检查每个索引是否有一个搜索字符串等于从当前索引开始的字符串。如果找到匹配项,则将替换项推送到缓冲区中,然后按匹配字符串的长度继续索引。如果未找到匹配项,则将索引加一。最后,将缓冲区中的字符串连接成一个字符串。

def strtr(strng, replace):
    buffer = []
    i, n = 0, len(strng)
    while i < n:
        match = False
        for s, r in replace.items():
            if strng[i:len(s)+i] == s:
                buffer.append(r)
                i = i + len(s)
                match = True
                break
        if not match:
            buffer.append(strng[i])
            i = i + 1
    return ''.join(buffer)
于 2012-06-07T12:10:53.897 回答
2

str.translate是等价的,但只能映射到单个字符。

于 2012-06-07T11:47:33.470 回答
0

该线程上的答案已经过时了。开始了...

选项 #1:使用 str.format() 函数来处理这个问题:

"Hello there {first_name} {last_name}".format(first_name="Bob", last_name="Roy")

选项 #2:使用模板类

from string import Template
t = Template('Hello there $first_name $last_name')
t.substitute(first_name="Bob", last_name="Roy")

参考: Python 字符串格式化最佳实践

于 2018-05-21T11:30:55.260 回答