Bir dizenin birden çok alt dizesi nasıl değiştirilir?


284

Birden çok dizeyi değiştirmek için .replace işlevini kullanmak istiyorum.

Şu anda sahibim

string.replace("condition1", "")

ama böyle bir şeye sahip olmak istiyorum

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

bu iyi bir sözdizimi gibi hissetmese de

Bunu yapmanın uygun yolu nedir? grep / regex'te nasıl yapabileceğiniz \1ve \2alanları belirli arama dizeleriyle nasıl değiştireceğiniz gibi


7
Sunulan tüm çözümleri denediniz mi? Hangisi daha hızlı?
tommy.carstensen

Farklı senaryolardaki tüm cevapları test etmek için zaman ayırdım. Bkz. Stackoverflow.com/questions/59072514/…
Pablo

1
Dürüst olmak gerekirse, zincirleme yaklaşımınızı diğerlerine tercih ederim. Bir çözüm ararken buraya indim ve sizinkini kullandım ve gayet iyi çalışıyor.
frakman1

@ frakman1 +1. Bunun neden daha fazla oy verilmediğine dair bir ipucu yok. Diğer tüm yöntemler kodun okunmasını zorlaştırır. Değiştirilecek bir işlev geçirme dizileri olsaydı, bu işe yarardı. Ancak zincirleme yönteminiz en açık (en azından statik sayıda değiştirme ile)
IceFire

Yanıtlar:


269

İşte düzenli ifadelerle hile yapması gereken kısa bir örnek:

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)

Örneğin:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'

7
Değiştirme tek bir seferde gerçekleşir.
Andrew Clark

26
dkamins: çok zekice değil, olması gerektiği kadar akıllı bile değil ("|" ile birleşmeden önce anahtarlardan regex-escape yapmalıyız). bu neden aşırı mühendis değil? çünkü bu şekilde tek geçişte (= hızlı) yapıyoruz ve aynı anda tüm değiştirmeleri yapıyoruz, yerine "spamham sha".replace("spam", "eggs").replace("sha","md5")olmak gibi çatışmalardan kaçınıyoruz"eggmd5m md5""eggsham md5"
koyun uçan

8
@AndrewClark Son satırda neler olduğunu lambda ile açıklayabilirseniz çok memnun olurum.
mineraller

11
Merhaba, bu pasajın daha net bir versiyonuna sahip küçük bir gist oluşturdum. Ayrıca biraz daha verimli olmalı: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Python 3 için iteritems () yerine items () kullanın.
Jangari

127

Sadece küçük bir döngü işlevi yapabilirsiniz.

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

burada texttam dize ve dicsözlüktür - her tanım, terimle eşleşmenin yerini alacak bir dizedir.

Not : Python 3'te, iteritems()ile değiştirilmiştiritems()


Dikkat: Python sözlüklerinin yineleme için güvenilir bir sırası yoktur. Bu çözüm yalnızca aşağıdaki durumlarda sorununuzu çözer:

  • değiştirme sırası ilgisiz
  • değiştirme işleminin önceki değiştirmelerin sonuçlarını değiştirmesi uygun

Örneğin:

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

Olası çıktı # 1:

"Bu benim domuzum ve bu benim domuzum."

Olası çıktı # 2

"Bu benim köpeğim ve bu benim domuzum."

Olası bir düzeltme OrderedDict kullanmaktır.

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)

Çıktı:

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

Dikkatli olun # 2: Dizeniz textçok büyükse veya sözlükte çok sayıda çift varsa verimsizdir .


37
Farklı değiştirmeleri uygulama sırası önemli olacaktır - bu nedenle standart bir dikte kullanmak yerine bir OrderedDict- veya 2 tuples listesi kullanmayı düşünün .
slothrop

5
Bu, dizeyi iki kez yinelemeyi sağlar ... performanslar için iyi değildir.
Valentin Lorentz

6
Performans açısından, Valentin'in söylediklerinden daha kötü - dic'de öğeler olduğu kadar metni geçecek! 'Metin' küçükse, büyük metinler için korkunçsa iyi.
JDonner

3
Bu, bazı durumlar için iyi bir çözümdür. Örneğin, sadece 2 karakteri alt etmek istiyorum ve ikame tuşları herhangi bir değerle eşleşmediği için gittikleri sıralamayı umursamıyorum. Ama ne olduğunu netleştirmek istiyorum.
Nathan Garabedian

5
İlk yinelemeye yeni eklenen metin ikinci yinelemede eşleştirilebildiğinden, bunun beklenmedik sonuçlar verebileceğini unutmayın. Örneğin, saf olarak tüm 'A' ile 'B' ve tüm 'B' ile 'C' yerine geçmeye çalışırsak, 'AB' dizesi 'BC' ye değil, 'CC'ye dönüştürülür.
Ambroz Bizjak

106

Neden böyle bir çözüm olmasın?

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

2
Bu süper kullanışlı, basit ve taşınabilir.
Shred

Güzel görünüyordu, ancak aşağıdaki gibi normal ifadenin yerini almıyor: for r in ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
1 astar yapmak için: s (= "kahverengi", "kırmızı"), ("tembel", "hızlı"))] [0]
Mark K

95

İşlevsel olmayı seviyorsanız, azaltma kullanan ilk çözümün bir çeşidi. :)

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

martineau'nun daha iyi versiyonu:

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

8
replsBir dizi tuples yapmak ve iteritems()çağrı ile ortadan kaldırmak daha kolay olurdu . yani repls = ('hello', 'goodbye'), ('world', 'earth')ve reduce(lambda a, kv: a.replace(*kv), repls, s). Ayrıca Python 3'te değişmeden çalışır.
martineau

Güzel! Eğer python3 kullanıyorsanız iteritems yerine öğeleri kullanın (şimdi dicts şeyler kaldırıldı).
e.arbitrio

2
@martineau: Bu reducekaldırıldığı için python3'te değişmeden çalıştığı doğru değil .
normanius

5
@normanius: reducehala ancak bir bölümünü yapıldığı, var olan functoolsmodül (bkz docs ı değişmeden dediğinde bu yüzden, ben aynı kod çalışma her ne kadar olabilir kuşkusuz o gerektirecektir geliyordu, Python 3'te) reduceolmuştur importgerekirse ed çünkü artık yerleşik değil.
martineau

35

Bu, FJ ve MiniQuark'ın harika cevaplarının daha kısa bir özeti. Birden fazla eşzamanlı dize değiştirmesi elde etmek için ihtiyacınız olan tek şey aşağıdaki işlevdir:

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)

Kullanımı:

>>>multiple_replace("Do you like cafe? No, I prefer tea.", {'cafe':'tea', 'tea':'cafe', 'like':'prefer'})
'Do you prefer tea? No, I prefer cafe.'

İsterseniz, bu daha basit olandan başlayarak kendi özel değiştirme işlevlerinizi yapabilirsiniz.


1
Bu iyi bir çözüm olmakla birlikte, eşzamanlı dize değiştirme işlemleri, bunları sıralı olarak gerçekleştirme (zincirleme) ile aynı sonuçları vermeyecektir, ancak bu önemli olmayabilir.
martineau

2
Elbette, rep_dict = {"but": "mut", "mutton": "lamb"}dize kodunuzla "button"sonuçlanır "mutton", ancak "lamb"yedeklemeler birbiri ardına zincirlenmişse verir .
martineau

2
Bu, kodun ana özelliği, bir kusur değil. Zincirleme değiştirmelerle, örneğimdeki gibi iki kelimeyi aynı anda ve karşılıklı olarak ikame etmek için istenen davranışı elde edemedi.
mmj

1
İhtiyacınız yoksa harika bir özellik gibi görünmüyordu. Ancak burada eşzamanlı değiştirmelerden bahsediyoruz , o zaman gerçekten ana özellik. "Zincirleme" değiştirmelerde, örneğin çıktısı Do you prefer cafe? No, I prefer cafe.hiç arzu edilmeyen bir sonuç olacaktır .
mmj

@David kendi cevabınızı yazın, düzenlemeniz çok radikal
UmNyobe

29

Bunu FJ'lerin mükemmel cevabı üzerine inşa ettim:

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)

Tek seferlik kullanım:

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

Değişim sadece bir geçişte yapıldığından, "kafe" nin "çay" olarak değiştiğini, ancak "kafe" olarak değişmediğini unutmayın.

Aynı değiştirme işlemini birçok kez yapmanız gerekirse, kolayca bir değiştirme işlevi oluşturabilirsiniz:

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

İyileştirmeler:

  • kodu bir fonksiyona dönüştürdü
  • çok satırlı destek eklendi
  • kaçarken bir hata düzeltildi
  • belirli bir çoklu değiştirme için bir işlev oluşturmak kolay

Zevk almak! :-)


1
Bazıları benim gibi python noobs için bu adım adım açıklayabilir misiniz?
Julian Suarez

Python noob dostum, bu yüzden onu anlamak için eksik bir atış yapacağım .. key_values ​​değiştirilecek öğelere ("|" ile birleştirilen anahtarlar) ve mantığa (eşleşme bir anahtarsa, dönüş değeri) bölün. b. regex ayrıştırıcısı yapın (anahtarları arayan ve verilen mantığı kullanan "desen") - bunu lambda işlevine sarın ve geri dönün. Şimdi aradığım şeyler: re.M ve değiştirme mantığı için lambda gerekliliği.
Fox

1
@Fox anladın. Bir lambda kullanmak yerine bir işlev tanımlayabilirsiniz, sadece kodu kısaltmaktır. Ancak pattern.sub, yalnızca bir parametreli (değiştirilecek metin) bir işlev beklediğini unutmayın , bu nedenle işlevin erişmesi gerekir replace_dict. re.MÇok satırlı değiştirmelere izin verir (dokümanda iyi açıklanmıştır: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Dize şablonlarının kullanımını önermek istiyorum. Değiştirilecek dizeyi sözlüğe yerleştirmeniz yeterlidir! Docs.python.org'dan örnek

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

İyi görünüyor, ancak sağlanmayan bir anahtar eklerken substitutebir istisna ortaya çıkar, bu nedenle kullanıcılardan şablon alırken dikkatli olun.
Bart Friederichs

2
Bu yaklaşımın bir dezavantajı, şablonun değiştirilecek $ dizelerinin tümünü ve hepsinden fazlasını içermemesi gerektiğidir, buraya
RolfBly

17

Benim durumumda, benzersiz anahtarların adlarla basit bir şekilde değiştirilmesine ihtiyacım vardı, bu yüzden bunu düşündüm:

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

3
Bu, yedek bir çatışmanız olmadığı sürece çalışır. Eğer değiştirdiyseniz iile ssize garip davranışları tanınacak.
bgusach

1
Sıra önemliyse, yukarıdaki dikte yerine bir dizi kullanabilirsiniz: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) O zaman dizi çiftlerinizi sipariş etmeye dikkat ediyorsanız, () öğesini tekrar tekrar değiştirmediğinizden emin olabilirsiniz.
KOD-READ

Görünüşe göre diktiler Python 3.7.0'dan düzeni koruyor . Test ettim ve en son kararlı Python 3 ile makinemde çalışıyor.
James Koss

15

Başlangıç Python 3.8ve tanıtılması atama ifadeleri (PEP 572) ( :=operatör), bir liste anlayışı içinde değiştirmeler uygulayabilirsiniz:

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

Bunun bir döngüde replace kullanmaktan daha verimli olup olmadığını biliyor musunuz? Tüm yanıtları performans için test ediyorum, ancak henüz 3.8'e sahip değilim.
Pablo

Çıktıyı neden bir listeye alıyorum?
johnrao07

1
@ johnrao07 Bir liste kavraması bir liste oluşturur. Bu yüzden, bu durumda, anlarsın ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Ancak, atama ifadesi ( text := text.replace) de, textonu değiştirerek yeni sürümlerini tekrarlar Liste anlaşıldıktan sonra, textdeğiştirilen metni içeren değişkeni kullanabilirsiniz .
Xavier Guihot

1
Yeni sürümünü texttek satırlık olarak döndürmek isterseniz , liste kavramasının son öğesini ayıklayan [text := text.replace(a, b) for a, b in replacements][-1](notu [-1]) da kullanabilirsiniz ; yani son sürümü text.
Xavier Guihot

13

İşte benim 0.02 $. Andrew Clark'ın cevabına dayanıyor, biraz daha net ve ayrıca değiştirilecek bir dizenin, değiştirilecek başka bir dizenin bir alt dizesi olduğu durumu da (daha uzun dize kazanıyor)

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)

Bu ise bu özünden , herhangi bir öneri varsa bunu değiştirmek için çekinmeyin.


1
Bunun yerine, kabul edilen cevap bu olmalıydı, çünkü normal ifade tüm anahtarlardan uzunluk azalan düzende sıralanarak ve | regex dönüşüm operatörü. Ve sıralama, herhangi bir alternatif varsa mümkün olan en uzun seçeneklerin seçilmesi için gereklidir.
Sachin S

Sıralama sayesinde bunun en iyi çözüm olduğunu kabul ediyorum. Sıralamanın yanı sıra orijinal cevabımla aynıdır, bu yüzden kimsenin böyle önemli bir özelliği kaçırmayacağından emin olmak için çözümüm için de ödünç aldım.
mmj

6

Değiştirilecek dizelerin düzenli ifadeler olabileceği bir çözüme ihtiyacım vardı, örneğin birden çok boşluk karakterini tek bir karakterle değiştirerek uzun bir metni normalleştirmeye yardımcı olmak için. MiniQuark ve mmj de dahil olmak üzere diğerlerinden gelen bir cevaplar zinciri üzerine inşa ettiğim şey şuydu:

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)

Diğer cevaplarda verilen örnekler için çalışır, örneğin:

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

Benim için asıl önemli olan, düzenli ifadeleri de kullanabilmenizdir, örneğin sadece tüm kelimeleri değiştirmek veya beyaz alanı normalleştirmek için:

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

Sözlük tuşlarını normal dizeler olarak kullanmak istiyorsanız, örneğin bu işlevi kullanarak multiple_replace öğesini çağırmadan önce bunlardan kaçabilirsiniz:

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"

Aşağıdaki işlev, sözlük anahtarlarınız arasında hatalı düzenli ifadeler bulmanıza yardımcı olabilir (multiple_replace hata mesajı çok açık olmadığından):

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

Değiştirmeleri zincirlemediğini, bunun yerine aynı anda gerçekleştirdiğini unutmayın. Bu, yapabileceklerini kısıtlamadan daha verimli hale getirir. Zincirin etkisini taklit etmek için, daha fazla dize değiştirme çifti eklemeniz ve çiftlerin beklenen sırasını sağlamanız gerekebilir:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'

Bu güzel, teşekkürler. Değişikliklerde geri başvuruların kullanılmasına izin verecek şekilde geliştirilebilir mi? Bunu nasıl ekleyeceğimi hemen anlayamadım.
cmarqu

Yukarıdaki sorumun cevabı stackoverflow.com/questions/45630940/…
cmarqu

4

İşte birçok küçük değiştirme ile uzun dizelerde daha verimli bir örnek.

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)

Mesele uzun dizelerin birçok birleşiminden kaçınmaktır. Kaynak dizeyi parçalara ayırırız, listeyi oluştururken bazı parçaların yerini alırız ve sonra her şeyi bir dizeye geri ekleriz.


2

Gerçekten bu şekilde yapmamalısınız, ama sadece çok havalı buluyorum:

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

Şimdi, answertüm değişikliklerin sonucu

yine, bu çok acayip ve düzenli kullanmanız gereken bir şey değil. Ama ihtiyacınız olduğunda böyle bir şey yapabileceğinizi bilmek güzel.


2

Ben de bu problemle mücadele ediyordum. Birçok ikame ile düzenli ifadeler mücadele eder ve döngüden string.replace( yaklaşık dört kat daha yavaştır) (deney koşullarında).

Kesinlikle Flashtext kitaplığını kullanmayı denemelisiniz ( blog yazısı burada , Github burada ). Benim durumumda bitti biraz büyüklük iki siparişler 0.015 s 1.8 sn den daha hızlı (düzenli ifadeler 7.7 saniye sürede) her belge için.

Yukarıdaki bağlantılarda kullanım örnekleri bulmak kolaydır, ancak bu çalışan bir örnektir:

    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'in tek bir geçişte değişiklik yaptığını unutmayın ( a -> b ve b -> c'nin 'a' nın ' c'ye çevrilmesini önlemek için ). Flashtext da (böylece 'inci Eşleşmeyecek 'is' bütün kelimeler arar olduğunu '). Hedefiniz birkaç kelime ise işe yarar ('Bu' kelimesini 'Merhaba' ile değiştirmek).


HTML etiketlerini değiştirmeniz gerekirse bu nasıl çalışır? Örneğin yerini <p>ile /n. Yaklaşımınızı denedim ama etiketleri ile flashtext ayrıştırmak gibi görünmüyor?
alias51

1
Neden beklediğiniz gibi çalışmadığından emin değilim. Bir olasılık, bu etiketlerin boşluklarla ayrılmaması ve Flashtext'in tüm kelimeleri aradığını unutmayın. Bunun bir yolu önce basit bir değiştirme kullanmaktır, böylece "Merhaba <p> orada" "Merhaba <p> orada" olur. İşiniz bittiğinde istenmeyen alanları kaldırmaya dikkat etmeniz gerekir (ayrıca basit değiştirme?). Umarım yardımcı olur.
Pablo

Teşekkürler, bir kelimenin sonunu işaretleyebilir <ve >işaretleyebilirsiniz (ancak değiştirmeye dahil edilebilir)?
alias51

1
"Kelimeler" in sadece boşluklarla işaretlendiğine inanıyorum. Belki de "KeywordProcessor" içinde ayarlayabileceğiniz bazı isteğe bağlı parametreler vardır. Aksi takdirde yukarıdaki yaklaşımı göz önünde bulundurun: "<" yerine "<" kullanın, Flashtext uygulayın ve sonra geri değiştirin (örneğin, "<" ila "<" ve "\ n" ila "\ n" işe yarayabilir).
Pablo

2

Bu sorunun tamlık için tek satırlı yinelemeli bir lambda işlevi cevabına ihtiyacı olduğunu hissediyorum. Bu yüzden orada:

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

Kullanımı:

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

Notlar:

  • Bu giriş sözlüğünü tüketir.
  • Python 3.6'dan itibaren anahtar sırasını korur; diğer cevaplardaki karşılık gelen uyarılar artık geçerli değildir. Geriye dönük uyumluluk için, tuple tabanlı bir versiyona başvurulabilir:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Not: Python'daki tüm özyinelemeli işlevlerde olduğu gibi, çok büyük özyineleme derinliği (yani çok büyük değiştirme sözlükleri) bir hataya neden olacaktır. Örneğin buraya bakınız .


Büyük bir sözlük kullanırken RecursionError ile karşılaşıyorum!
Pablo

@Pablo İlginç. Ne kadar büyük? Bunun tüm özyinelemeli işlevler için olduğunu unutmayın. Örnek için buraya bakın: stackoverflow.com/questions/3323001/…
mcsoini

Değiştirmelerin Benim Sözlük bugüne kadar String.Replace kullanıyor ... 100k açısından yakın arayla en iyi yaklaşımı.
Pablo

1
@Pablo bu durumda özyinelemeli işlevleri kullanamazsınız. Genel olarak, sys.getrecursionlimit()bir çift 1000, maks. bir döngü veya bunun gibi bir şey kullanın veya ikameleri basitleştirmeye çalışın.
19:40

Evet, korkarım burada gerçekten kısayol yok.
Pablo

1

Hızı bilmiyorum ama bu benim geçici işim hızlı düzeltme:

reduce(lambda a, b: a.replace(*b)
    , [('o','W'), ('t','X')] #iterable of pairs: (oldval, newval)
    , 'tomato' #The string from which to replace values
    )

... ama yukarıdaki 1 numaralı normal yanıtı beğeniyorum. Not - yeni bir değer diğerinin alt dizesi ise, işlem değişmeli değildir.


1

pandasKitaplığı ve replacehem tam eşleşmeleri hem de normal ifadeleri değiştirmeyi destekleyen işlevi kullanabilirsiniz . Örneğin:

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

Ve değiştirilen metin:

0    name is going to visit city in month
1                      I was born in date
2                 I will be there at time

Burada bir örnek bulabilirsiniz . Metindeki değiştirmelerin listelerde göründükleri sıraya göre yapıldığına dikkat edin


1

Yalnızca bir karakteri değiştirmek için translateve str.maketransyöntemini kullanın.

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


gösteri

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.

0

Andrew'un değerli cevabından başlayarak, sözlüğü bir dosyadan yükleyen ve değiştirilen dosyaları yapmak için açılan klasördeki tüm dosyaları hazırlayan bir komut dosyası geliştirdim. Komut dosyası, eşleştirmeleri ayırıcıyı ayarlayabileceğiniz harici bir dosyadan yükler. Ben bir acemi değilim ama ben birden fazla dosya birden fazla yerine yaparken bu komut dosyası çok yararlı buldum. Saniyede 1000'den fazla girişi olan bir sözlük yükledi. Zarif değil ama benim için çalıştı

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

bu benim sorunun çözümü. Bir sohbette farklı kelimeleri bir kerede değiştirmek için kullandım.

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"})

bu olacak The cat hunts the dog


0

Başka bir örnek: Giriş listesi

error_list = ['[br]', '[ex]', 'Something']
words = ['how', 'much[ex]', 'is[br]', 'the', 'fish[br]', 'noSomething', 'really']

İstenen çıktı

words = ['how', 'much', 'is', 'the', 'fish', 'no', 'really']

Kod:

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

-2

Ya da sadece hızlı bir kesmek için:

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)

-2

İşte bir sözlükle yapmanın başka bir yolu:

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.