IT story

문자열의 여러 하위 문자열을 바꾸는 방법은 무엇입니까?

hot-time 2020. 4. 10. 08:19
반응형

문자열의 여러 하위 문자열을 바꾸는 방법은 무엇입니까?


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

나는 현재

string.replace("condition1", "")

하지만 같은 것을 갖고 싶습니다

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

좋은 문법처럼 보이지는 않지만

이를 수행하는 올바른 방법은 무엇입니까? 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()으로 대체되었습니다.items()


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

  • 교체 순서는 관련이 없습니다.
  • 교체가 이전 교체의 결과를 변경해도 괜찮습니다.

예를 들어 :

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

가능한 출력 # 1 :

"이것은 나의 돼지이고 이것은 나의 돼지입니다."

가능한 출력 # 2

"이것은 나의 개이고 이것은 나의 돼지입니다."

하나의 가능한 수정은 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 문자열이 너무 크거나 사전에 많은 쌍이있는 경우 비효율적 입니다.


다음은 기능을 좋아하는 경우 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)

왜 이런 해결책이 없습니까?

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

이것은 FJ와 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.'

원하는 경우이 간단한 것부터 시작하여 고유 한 전용 교체 기능을 만들 수 있습니다.


나는 이것을 FJ의 훌륭한 답변을 바탕으로 구축했습니다.

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

교체는 한 번만 수행되므로 "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!\"

개량:

  • 코드를 함수로 바꿨다
  • 여러 줄 지원 추가
  • 탈출 버그 수정
  • 특정 복수 교체를위한 기능을 쉽게 생성

즐겨! :-)


문자열 템플릿의 사용법을 제안하고 싶습니다. 문자열을 사전에 대치하면 모든 것이 설정됩니다! 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'

필자의 경우 고유 키를 이름으로 간단히 바꾸어야했기 때문에 다음과 같이 생각했습니다.

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)

그것은이에 이 요점 , 당신이 어떤 제안이있는 경우 수정 주시기 바랍니다.


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

예를 들어 여러 공백 문자를 단일 문자로 바꾸어 긴 텍스트를 정규화하는 데 도움이되는 대체 문자열이 정규식이 될 수있는 솔루션이 필요했습니다. 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)

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


나는 속도에 대해 모른다. 그러나 이것은 나의 임시 빠른 수정이다.

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 정규식 답변이 마음에 듭니다. 주 – 하나의 새로운 값이 다른 값의 하위 문자열 인 경우 연산은 정식이 아닙니다.


당신은 정말로 이런 식으로하지 말아야하지만 너무 멋지다.

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

이제 answer모든 교체의 결과는

다시 말하지만, 이것은 매우 해킹이며 정기적으로 사용해야하는 것이 아닙니다. 그러나 필요한 경우 이와 같은 작업을 수행 할 수 있다는 것을 아는 것이 좋습니다.


또는 빠른 해킹의 경우 :

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)

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

예를 들어 코드는 다음과 같아야합니다.

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

요청 된대로 모든 변경 사항을 인쇄합니다.


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

여기 에서 예를 찾을 수 있습니다 . 텍스트의 대체는 목록에 표시된 순서대로 수행됩니다.

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

반응형