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のように動作させるにはどうすればよいですか?

(可能であれば、正規表現のない回答を希望します)。

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

私自身のバージョン(わずかに最適化されたガンボのものです):

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

単純なアルゴリズムは次のとおりです。

インデックスを使用して元の文字列を 1 文字ずつ調べ、各インデックスについて、検索文字列の 1 つが現在のインデックスの文字列と等しいかどうかを確認します。一致が見つかった場合は、置換をバッファにプッシュし、一致した文字列の長さだけインデックスを進めます。一致するものが見つからない場合は、インデックスを 1 つ進めます。最後に、バッファ内の文字列を 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)
于 2012-06-07T12:10:53.897 に答える
2

str.translateは同等ですが、単一の文字にしかマップできません。

于 2012-06-07T11:47:33.470 に答える