php';python的strtr

php';python的strtr,python,string,Python,String,php具有以下功能: 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)

php具有以下功能:

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对应函数类似

(如果可能的话,我希望答案没有正则表达式)

Upd:这里有一些很棒的答案。我对它们进行了计时,发现对于短字符串,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,更换):
buf,i=[],0
而我

完整的代码和计时:

是等效的,但只能映射到单个字符。

下面是一个简单的算法:

使用索引逐个字符遍历原始字符串,并检查每个索引中的一个搜索字符串是否等于上的当前索引中的字符串。如果找到匹配项,则将替换项推入缓冲区,并按匹配字符串的长度继续索引。如果找不到匹配项,则按1继续索引。最后,将缓冲区中的字符串连接到单个字符串

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)
def strtr(strng,更换):
缓冲区=[]
i、 n=0,len(strng)
而i
以下内容使用正则表达式执行此操作:

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版本一样,这会优先考虑较长的匹配。

此线程上的答案太过时了。我们走吧

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
选项1:使用str.format()函数处理此问题: 选项2:使用模板类 参考:

我们都忽略了这一点(从strtr文档中):将首先尝试最长的键。不,它不优先选择较长的匹配,这取决于字典键的任意顺序:
strtr('xxa-bb-cc',{'xx':'bbz','xxa':'bby')
->
'bbza-bb-cc'
。用
sorted(repl.keys(),key=len,reverse=True)
代替
repl.keys()
应该可以解决这个问题。@Duncan:真令人惊讶,谢谢你指出(我一直认为Python的
re
匹配时间最长,但显然不是这样)重复
x*
x+
x?
x{m,n}
都是贪婪的,所以他们会尽可能多地重复
x
x*?
x+?
x???
x{m,n}?
都是非贪婪的,所以它们的匹配尽可能短<代码> xyyy/c>也不贪婪,因为如果<代码> x<代码>匹配,引擎甚至不会考虑<代码> y>代码>。这就是这里发生的事情:交替严格地从左到右进行测试,并在找到匹配项后立即停止。@Duncan:这是有意义的,感谢您的澄清(我知道贪婪和非贪婪重复,但不知道
操作符)。谢谢,这个解决方案在较长的主题字符串上似乎是最快的(见更新)。看起来很酷,但是进行
1+2+…+len(repl)
递归调用…我不知道。嘿,你要求的是一个行为类似php的非正则表达式版本,你没有要求快速。;(此外,我怀疑复制dict比递归调用更糟糕。)@kojiro:很公平。你肯定会在这条线中获得美貌奖。太糟糕了,我不能接受多个答案…顺便说一句,你为什么用
***
而不是
dict(replace)
?没有好的理由。不好的原因是我没有花时间检查dict(adict)是否会复制,而只是返回相同的dict.-更新的答案代码。
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
"Hello there {first_name} {last_name}".format(first_name="Bob", last_name="Roy")
from string import Template
t = Template('Hello there $first_name $last_name')
t.substitute(first_name="Bob", last_name="Roy")