web-dev-qa-db-ja.com

文字列の複数の部分文字列を置き換える方法

複数の文字列を置き換えるには、.replace関数を使用します。

私は現在持っています

string.replace("condition1", "")

しかし、何かが欲しいのですが

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

それは良い構文のようには感じないが

これを行うための適切な方法は何ですか? grep/regexで\1\2を使ってフィールドを特定の検索文字列に置き換える方法のようなものです。

215
CQM

これは、正規表現を使ったトリックの簡単な例です。

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.iteritems()) 
#Python 3 renamed dict.iteritems to dict.items so use rep.items() for latest versions
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--'
220
Andrew Clark

あなたはちょうどいい小さなループ関数を作ることができます。

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の辞書には信頼できる繰り返し順はありません。この解決策はあなたの問題を解決する場合に限ります:

  • 交換の順序は関係ありません
  • 以前の交換の結果を変更して交換することは問題ありません

例えば:

d = { "cat": "dog", "dog": "pig"}
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, d)
print(mySentence)

考えられる出力※1

「これは私の豚です。これは私の豚です。」

考えられる出力※2

「これは私の犬です、これは私の豚です。」

考えられる解決策の1つは、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")])
mySentence = "This is my cat and this is my dog."
replace_all(mySentence, od)
print(mySentence)

出力:

"This is my pig and this is my pig."

注意#2:あなたのtext文字列が大きすぎるか、辞書にたくさんのペアがある場合は非効率です。

101
Joseph Hansen

これはあなたが機能的であることを好む場合に備えて、reduceを使った最初の解決策の変形です。 :)

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

なぜこのような解決策がないのでしょうか。

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
66
Enrico Bianchi

これはF.JとMiniQuarkの素晴らしい答えをより簡潔にまとめたものです。 複数の同時文字列置換を達成するために必要なのは、次の関数だけです。

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

ご希望の場合は、この単純なものから始めて独自の専用の置換機能を作成することができます。

29
mmj

私はこれをF.Jの優れた答えに基づいて構築しました。

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

置き換えは1回のパスで行われるため、 "café"は "tea"に変わりますが、 "café"に戻ることはありません。

同じ置換を何度も行う必要がある場合は、置換関数を簡単に作成できます。

>>> my_escaper = multiple_replacer(('"','\\"'), ('\t', '\\t'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?\tYes it does',
                       u'And can we span\nmultiple lines?\t"Yes\twe\tcan!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?\tYes it does
And can we span
multiple lines?\t\"Yes\twe\tcan!\"

改善点

  • コードを関数に変える
  • 複数行サポートを追加
  • エスケープのバグを修正しました
  • 特定の複数置換用の関数を簡単に作成できます

お楽しみください。 : - )

28
MiniQuark

文字列テンプレートの使い方を提案したいと思います。置き換える文字列を辞書に入れるだけで、すべてが設定されます。 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'
19
Fredrik Pihl

私の場合は、一意のキーを名前に簡単に置き換える必要があるので、次のように考えました。

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.'
10
James Koss

ここで私の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)

これは この要旨 です。何か提案があればお気軽に修正してください。

8
bgusach

たとえば、複数の空白文字を1つの空白文字に置き換えることによって長いテキストを正規化するために、置き換える文字列を正規表現にすることができる解決策が必要でした。 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:\n  Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> 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:\n  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'
5
user2443147

Python 3.8から始めて、 代入式(PEP 572):=演算子)を導入すると、リスト内で置換を適用できます。

# 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'
3
Xavier Guihot

コードは次のようにすることをお勧めします。

z = "My name is Ahmed, and I like coding "
print(z.replace(" Ahmed", " Dauda"). replace(" like", " Love" ))

要求どおりにすべての変更を印刷します。

2
Ahmed dauda

あなたは本当にこのようにそれをするべきではありませんが、私はちょうどそれがあまりにもクールだと思います:

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

さて、answerは順番にすべての置き換えの結果です。

繰り返しますが、これは非常にハッキーであり、あなたが定期的に使うべきものではありません。しかし、必要に応じてこのようなことができることを知っているのは嬉しいことです。

1
inspectorG4dget

私はスピードについて知りませんが、これは私の仕事日の迅速な修正です:

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正規表現の答えが好きです。注 - 1つの新しい値が別の値のサブストリングである場合、その操作は可換ではありません。

1
del_hol

これは、多くの小さな置換がある長い文字列でより効率的なサンプルです。

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)

重要なのは、長い文字列の多くの連結を避けることです。ソース文字列をフラグメントに切り、リストを形成するときにフラグメントの一部を置き換えてから、全体を結合してストリングに戻します。

1
9000

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

例を見つけることができます here 。テキストの置換は、リストに表示される順序で行われます。

0
George Pipis

これが私の問題解決です。私はチャットボットでそれを使って一度に違う単語を置き換えました。

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になります

0
emorjon2

他の例:入力リスト

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]] 
0
Akhil Thayyil

これは辞書でそれをするもう一つの方法です:

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

あるいは速いハックのためだけに:

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)
0
Brandon H

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('\n').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()
0
Tommaso Sandi