source

문자열의 여러 서브스트링을 대체하려면 어떻게 해야 합니까?

lovecheck 2022. 10. 30. 17:58
반응형

문자열의 여러 서브스트링을 대체하려면 어떻게 해야 합니까?

.replace 함수를 사용하여 여러 문자열을 교체하고 싶습니다.

저는 현재

string.replace("condition1", "")

이런 걸 갖고 싶어요

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

그것이 좋은 구문처럼 느껴지지 않지만

GREP/GREP/regex를 실행할 수 있는지와 비슷합니다.\1 ★★★★★★★★★★★★★★★★★」\2를 특정

다음은 정규 표현으로 문제를 해결할 수 있는 간단한 예입니다.

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

그냥 작고 멋진 루프 기능을 만들 수 있어요

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text

서 ''는text과 """입니다.dic을 사용하다각 정의는 용어의 일치를 대체하는 문자열입니다.

주의: Python 3에서는iteritems() has has has has has has has has has has has 로 바뀌었습니다.items()


주의: Python 사전에는 신뢰할 수 있는 반복 순서가 없습니다.이 솔루션은 다음과 같은 경우에만 문제를 해결합니다.

  • 교체 순서는 무관합니다.
  • 이전 교체의 결과를 변경할 수 있습니다.

업데이트: 삽입 순서와 관련된 위의 문장은 3.6 이상의 Python 버전에는 적용되지 않습니다.이것은 반복에 삽입 순서를 사용하도록 표준 dits가 변경되었기 때문입니다.

예:

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

가능한 출력 #1:

"이건 내 돼지이고 이건 내 돼지야."

가능한 출력 #2

"이건 내 개고 이건 내 돼지야."

가능한 수정 방법 중 하나는 Ordered Dict를 사용하는 것입니다.

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")])
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, od)
print(my_sentence)

출력:

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

주의사항 2: 효율이 저하된 경우text문자열이 너무 크거나 사전에 쌍이 많이 있습니다.

왜 이런 해결책이 없을까?

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

다음은 기능성을 원하는 경우에 대비하여 reduce를 사용하는 첫 번째 솔루션의 변형입니다.:)

repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)

마티노의 더 나은 버전:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)

이것은 단지 F의 더 간결한 요약이다.J와 MiniQuark의 훌륭한 답변과 bgusach에 의한 마지막이지만 결정적인 개선.여러 개의 문자열을 동시에 치환하기 위해 필요한 것은 다음 기능뿐입니다.

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

필요에 따라서, 이 간단한 기능으로부터 전용의 치환 기능을 작성할 수 있습니다.

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

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

교환은 한 번에 이루어지기 때문에 "카페"는 "티"로 변경되지만 다시 "카페"로 변경되지 않습니다.

같은 치환을 여러 번 수행해야 하는 경우 치환 기능을 쉽게 만들 수 있습니다.

>>> 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!\"

개선점:

  • 코드를 함수로 바꾸다
  • 멀티라인 지원 추가
  • 탈옥의 오류를 고치다
  • 특정 다중 치환 기능을 쉽게 만들 수 있는

즐기세요! :-)

저는 문자열 템플릿의 사용을 제안하고 싶습니다.교체할 문자열을 사전에 배치하기만 하면 모든 것이 설정됩니다!문서의 예.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'

제 경우 고유 키를 이름으로 간단하게 교체해야 했기 때문에 다음과 같이 생각했습니다.

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

여기 제 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)

요지에서는 제안사항이 있으시면 언제든지 수정해 주십시오.

예를 들어 여러 개의 공백 문자를 하나의 공백 문자로 대체함으로써 긴 텍스트를 정규화하는 데 도움이 되는 대체 문자열이 필요한 솔루션이 필요했습니다.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'

주의: 사례를 테스트하고 의견을 참조하십시오.

여기 작은 치환이 많은 긴 현에서 더 효율적인 샘플이 있습니다.

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)

요점은 긴 문자열의 많은 연결을 피하는 것입니다.소스 문자열을 조각으로 자르고 목록을 만들 때 조각 일부를 교체한 다음 전체 문자열을 다시 문자열로 결합합니다.

.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

여기서 예를 찾을 수 있습니다.텍스트의 치환은 목록에 표시되는 순서로 이루어집니다.

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★에서 정규 하며, 약 느립니다.string.replace(이러한)

Flashtext 라이브러리(블로그 투고는 이쪽, Github은 이쪽)를 꼭 사용해 보세요.제 경우, 각 문서에 대해 1.8초에서 0.015초(정규 표현은 7.7초가 소요됨) 2배 이상 빨랐습니다.

위의 링크에서 사용 예를 쉽게 찾을 수 있지만, 이것은 실제적인 예입니다.

    from flashtext import KeywordProcessor
    self.processor = KeywordProcessor(case_sensitive=False)
    for k, v in self.my_dict.items():
        self.processor.add_keyword(k, v)
    new_string = self.processor.replace_keywords(string)

Flashtext는 (a --> b b --> c를 c로 변환하지 않기 위해) 단일 패스로 치환합니다.또한 플래시 텍스트는 전체 단어를 찾습니다(따라서 'is'는 'this'와 일치하지 않습니다).대상이 여러 단어인 경우('This is'를 'Hello'로 대체) 잘 작동합니다.

나는 학교 숙제 중 하나에서 비슷한 운동을 하고 있었다.이것이 나의 해결책이었다.

dictionary = {1: ['hate', 'love'],
              2: ['salad', 'burger'],
              3: ['vegetables', 'pizza']}

def normalize(text):
    for i in dictionary:
        text = text.replace(dictionary[i][0], dictionary[i][1])
    return text

테스트 문자열 결과 보기

string_to_change = 'I hate salad and vegetables'
print(normalize(string_to_change))

이 질문은 완전성을 위해 한 줄의 반복 람다 함수에 대한 답이 필요하다고 생각합니다.그래서 다음과 같이 됩니다.

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.popitem()), d)

사용방법:

>>> mrep('abcabc', {'a': '1', 'c': '2'})
'1b21b2'

주의:

  • 이것은 입력 사전을 소비합니다.
  • Python dicts는 3.6을 기준으로 키 순서를 유지합니다. 다른 답변에 대한 해당 경고는 더 이상 관련이 없습니다.하위 호환성을 위해 다음과 같은 태플 기반 버전을 사용할 수 있습니다.
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

참고: python의 모든 재귀 함수와 마찬가지로 재귀 깊이가 너무 크면(즉, 치환 사전이 너무 크면) 오류가 발생합니다.예: 여기를 참조하십시오.

저는 오늘 비슷한 문제에 직면해 있습니다.그때 .replace() 메서드를 여러 번 사용했는데 기분이 좋지 않았습니다.그래서 이렇게 했어요.

REPLACEMENTS = {'<': '&lt;', '>': '&gt;', '&': '&amp;'}

event_title = ''.join([REPLACEMENTS.get(c,c) for c in event['summary']])

이런 식으로 하면 안 되는데 너무 멋있어요.

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

그럼 이제 ㅇㅇㅇㄹㄹㄹㄹ,answer입니다.

다시 말씀드리지만, 이것은 매우 해킹이 심하고 정기적으로 사용해서는 안 되는 것입니다.하지만 당신이 필요할 때 이런 일을 할 수 있다는 것을 알게 되어 기쁩니다.

1개의 문자만 치환하려면translate그리고.str.maketrans내가 가장 좋아하는 방법이야.

tl;dr >result_string = your_string.translate(str.maketrans(dict_mapping))


데모

my_string = 'This is a test string.'
dict_mapping = {'i': 's', 's': 'S'}
result_good = my_string.translate(str.maketrans(dict_mapping))
result_bad = my_string
for x, y in dict_mapping.items():
    result_bad = result_bad.replace(x, y)
print(result_good)  # ThsS sS a teSt Strsng.
print(result_bad)   # ThSS SS a teSt StrSng.

속도에 대해서는 잘 모르지만, 이것은 나의 일과성 퀵픽스입니다.

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 regex 답변이 좋다.참고 - 새 값 중 하나가 다른 값의 하위 문자열일 경우 연산은 가환되지 않습니다.

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

이것이 그 문제에 대한 나의 해결책이다.나는 그것을 챗봇으로 사용하여 다른 단어들을 한번에 대체했다.

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

다른 예: 입력 목록

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]] 

우선 문자열을 토큰화한 후 각 토큰에 포함할지 여부를 결정합니다.

해시맵/세트의 O(1) 룩업을 상정할 수 있는 경우 퍼포먼스가 향상될 수 있습니다.

remove_words = {"we", "this"}
target_sent = "we should modify this string"
target_sent_words = target_sent.split()
filtered_sent = " ".join(list(filter(lambda word: word not in remove_words, target_sent_words)))

filtered_sent지금이다'should modify string'

다음은 기본 regex 치환을 지원하는 버전입니다.주요 제약사항은 표현식에 하위 그룹이 포함되어서는 안 되며 다음과 같은 가장자리 케이스가 있을 수 있습니다.

@bgusach 등에 근거한 코드

import re

class StringReplacer:

    def __init__(self, replacements, ignore_case=False):
        patterns = sorted(replacements, key=len, reverse=True)
        self.replacements = [replacements[k] for k in patterns]
        re_mode = re.IGNORECASE if ignore_case else 0
        self.pattern = re.compile('|'.join(("({})".format(p) for p in patterns)), re_mode)
        def tr(matcher):
            index = next((index for index,value in enumerate(matcher.groups()) if value), None)
            return self.replacements[index]
        self.tr = tr

    def __call__(self, string):
        return self.pattern.sub(self.tr, string)

테스트

table = {
    "aaa"    : "[This is three a]",
    "b+"     : "[This is one or more b]",
    r"<\w+>" : "[This is a tag]"
}

replacer = StringReplacer(table, True)

sample1 = "whatever bb, aaa, <star> BBB <end>"

print(replacer(sample1))

# output: 
# whatever [This is one or more b], [This is three a], [This is a tag] [This is one or more b] [This is a tag]

요령은 일치하는 그룹을 위치로 식별하는 것입니다.초효율(O(n))은 아니지만 동작합니다.

index = next((index for index,value in enumerate(matcher.groups()) if value), None)

교환은 한 번에 이루어집니다.

또는 빠른 해킹을 위해:

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)

다음은 사전을 사용하여 수행하는 다른 방법입니다.

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)
sentence='its some sentence with a something text'

def replaceAll(f,Array1,Array2):
    if len(Array1)==len(Array2):
        for x in range(len(Array1)):
            return f.replace(Array1[x],Array2[x])

newSentence=replaceAll(sentence,['a','sentence','something'],['another','sentence','something something'])

print(newSentence)

언급URL : https://stackoverflow.com/questions/6116978/how-to-replace-multiple-substrings-of-a-string

반응형