2014-12-21 7 views
9

Nur mit Python regulärer Ausdruck, wie findet und ersetzt man das n-te Auftreten eines Wortes in einem Satz? Zum Beispiel:Wie findet und ersetzt man das n-te Auftreten eines Wortes in einem Satz mit dem regulären Python-Ausdruck?

str = 'cat goose mouse horse pig cat cow' 
new_str = re.sub(r'cat', r'Bull', str) 
new_str = re.sub(r'cat', r'Bull', str, 1) 
new_str = re.sub(r'cat', r'Bull', str, 2) 

Ich habe einen Satz oben, wo das Wort ‚Katze‘ erscheint zweimal im Satz. Ich möchte, dass das zweite Vorkommen der "Katze" in "Bull" geändert wird, wobei das erste "Katzen" -Wort unverändert bleibt. Mein letzter Satz würde aussehen wie: "Katze Gans Maus Pferd Schwein Stier Kuh". In meinem Code oben versuchte ich 3 verschiedene Male konnte ich nicht bekommen was ich wollte.

+0

Ich denke, es ist besser, wenn Sie die teilen string, zähle das Vorkommen von 'cat' und gebe eine modifizierte Liste zurück, wobei die' nth' ersetzt wird. Könnte ein wenig langsamer sein, aber das ist vielleicht egal und wird definitiv lesbarer sein als ein haariger Regexp. –

Antwort

9

Verwenden Sie negative Lookahead wie unten.

>>> s = "cat goose mouse horse pig cat cow" 
>>> re.sub(r'^((?:(?!cat).)*cat(?:(?!cat).)*)cat', r'\1Bull', s) 
'cat goose mouse horse pig Bull cow' 

DEMO

  • ^ Behauptet, dass wir am Anfang sind.
  • (?:(?!cat).)* Entspricht einem beliebigen Zeichen, aber nicht cat, Null oder mehrmals.
  • cat entspricht dem ersten cat Teilstring.
  • (?:(?!cat).)* Entspricht einem beliebigen Zeichen, aber nicht cat, Null oder mehrmals.
  • Umschließen Sie nun alle Muster in einer Erfassungsgruppe wie ((?:(?!cat).)*cat(?:(?!cat).)*), damit wir diese erfassten Zeichen später weiterleiten können.
  • cat jetzt die folgende zweite cat Zeichenfolge ist abgestimmt.

OR

>>> s = "cat goose mouse horse pig cat cow" 
>>> re.sub(r'^((.*?cat.*?){1})cat', r'\1Bull', s) 
'cat goose mouse horse pig Bull cow' 

Ändern der Anzahl innerhalb der {} des ersten oder zweiten oder n-ten Auftreten der Zeichenfolge zu ersetzen cat

Um den dritten Auftreten des cat Zeichenkette ersetzt, legte 2 in den geschweiften Klammern ..

>>> re.sub(r'^((.*?cat.*?){2})cat', r'\1Bull', "cat goose mouse horse pig cat foo cat cow") 
'cat goose mouse horse pig cat foo Bull cow' 

Play with the above regex on here ...

+0

Hallo, was ist der Vorteil gegenüber der Verwendung von 'r '(cat. *?) Cat''? – Pierre

+0

Warum ein Downvote? –

+0

Also wie verdient es einen Downvote? Es ist keine falsche Antwort. –

0

Sie können die beiden Vorkommen von „cat“, passen alles halten vor dem zweiten Auftreten (\1) und fügen Sie „Bull“:

new_str = re.sub(r'(cat.*?)cat', r'\1Bull', str, 1) 

Wir machen nur eine Substitution der vierte zu vermeiden ersetzen, sechsten, usw. Auftreten von "Katze" (wenn es mindestens vier Vorkommen gibt), wie von Avinash Raj kommentiert.

Wenn Sie das n -te Auftreten und nicht die zweite, ersetzen verwenden:

n = 2 
new_str = re.sub('(cat.*?){%d}' % (n - 1) + 'cat', r'\1Bull', str, 1) 

BTW nicht str als Variablennamen verwenden sollten Sie, da es ein Python reservierte Schlüsselwort ist.

+1

Beachten Sie, dass Op den zweiten ändern möchte. Deines würde scheitern, wenn die Eingabe "Katze Katze Katze Gans Maus Katze" ist –

+0

dann warum hast du 'str' als Variablenname benutzt? –

+0

@ Avinash Raj: Ich habe die Variable in der Frage verwendet (und nicht betroffen). – Pierre

3

Hier ist eine Möglichkeit, es ohne einen regulären Ausdruck zu tun:

def replaceNth(s, source, target, n): 
    inds = [i for i in range(len(s) - len(source)+1) if s[i:i+len(source)]==source] 
    if len(inds) < n: 
     return # or maybe raise an error 
    s = list(s) # can't assign to string slices. So, let's listify 
    s[inds[n-1]:inds[n-1]+len(source)] = target # do n-1 because we start from the first occurrence of the string, not the 0-th 
    return ''.join(s) 

Verbrauch:

In [278]: s 
Out[278]: 'cat goose mouse horse pig cat cow' 

In [279]: replaceNth(s, 'cat', 'Bull', 2) 
Out[279]: 'cat goose mouse horse pig Bull cow' 

In [280]: print(replaceNth(s, 'cat', 'Bull', 3)) 
None 
0

ich eine Funktion definieren würde, die für jede Regex funktioniert:

import re 

def replace_ith_instance(string, pattern, new_str, i = None, pattern_flags = 0): 
    # If i is None - replacing last occurrence 
    match_obj = re.finditer(r'{0}'.format(pattern), string, flags = pattern_flags) 
    matches = [item for item in match_obj] 
    if i == None: 
     i = len(matches) 
    if len(matches) == 0 or len(matches) < i: 
     return string 
    match = matches[i - 1] 
    match_start_index = match.start() 
    match_len = len(match.group()) 

    return '{0}{1}{2}'.format(string[0:match_start_index], new_str, string[match_start_index + match_len:]) 

Ein Arbeitsbeispiel:

str = 'cat goose mouse horse pig cat cow' 
ns = replace_ith_instance(str, 'cat', 'Bull', 2) 
print(ns) 

Der Ausgang:

cat goose mouse horse pig Bull cow 

Ein weiteres Beispiel:

str2 = 'abc abc def abc abc' 
ns = replace_ith_instance(str2, 'abc\s*abc', '666') 
print(ns) 

Der Ausgang:

abc abc def 666 
0

Erstellen Sie eine Replikationsfunktion, die an re.sub() übergeben wird. Außer ... der Trick besteht darin, es zu einer Klasse zu machen, damit Sie die Anrufzahl verfolgen können.

class ReplWrapper(object): 
    def __init__(self, replacement, occurrence): 
     self.count = 0 
     self.replacement = replacement 
     self.occurrence = occurrence 
    def repl(self, match): 
     self.count += 1 
     if self.occurrence == 0 or self.occurrence == self.count: 
      return match.expand(self.replacement) 
     else: 
      try: 
       return match.group(0) 
      except IndexError: 
       return match.group(0) 

Dann ist es wie folgt verwenden:

myrepl = ReplWrapper(r'Bull', 0) # replaces all instances in a string 
new_str = re.sub(r'cat', myrepl.repl, str) 

myrepl = ReplWrapper(r'Bull', 1) # replaces 1st instance in a string 
new_str = re.sub(r'cat', myrepl.repl, str) 

myrepl = ReplWrapper(r'Bull', 2) # replaces 2nd instance in a string 
new_str = re.sub(r'cat', myrepl.repl, str) 

Ich bin sicher, dass es ein cleverer Weg, um eine Klasse zu vermeiden, verwenden, aber dies schien geradlinig genug zu erklären. Stellen Sie außerdem sicher, dass Sie match.expand() zurückgeben, da nur die Rückgabe des Ersatzwerts nicht technisch korrekt ist und jemand entscheidet, \1 Typvorlagen zu verwenden.

2

Ich benutze eine einfache Funktion, die alle Vorkommen auflistet, die n-te Position auswählt und sie benutzt, um die ursprüngliche Zeichenkette in zwei Teilstrings aufzuteilen. Dann ersetzt es das erste Vorkommen in der zweiten Teilkette und schließt sich Strings zurück in die neue Zeichenfolge:

import re 

def replacenth(string, sub, wanted, n) 
    where = [m.start() for m in re.finditer(sub, string)][n-1] 
    before = string[:where] 
    after = string[where:] 
    after.replace(sub, wanted, 1) 
    newString = before + after 
    print newString 

Für diese Variablen:

string = 'ababababababababab' 
sub = 'ab' 
wanted = 'CD' 
n = 5 

Ausgänge:

ababababCDabababab 

Hinweise:

Die where v Ariable ist tatsächlich eine Liste von Match-Positionen, wo Sie die n-te auswählen. Der Listeneingangsindex beginnt jedoch normalerweise mit 0, nicht mit 1. Daher gibt es eine n-1 Index und n Variable ist die tatsächliche nth Teilzeichenfolge. Mein Beispiel findet die 5. Zeichenfolge. Wenn Sie den Index n verwenden und die 5. Position suchen möchten, benötigen Sie n als 4. Was Sie verwenden, hängt normalerweise von der Funktion ab, die unsere n generiert.

Dies sollte der einfachste Weg sein, aber es ist nicht Regex nur so, wie Sie ursprünglich wollten.

Quellen und einige Links dazu:

0

Wie die n-te Nadel ersetzen Wort:

s.replace (Nadel, '$$$', n-1) .replace (Nadel, Wort, 1) .replace ('$$$', Nadel)

+0

Die Frage (ab 2014) fordert ausdrücklich die Verwendung eines Pythons an Regulärer Ausdruck, und hat eine Antwort, die der Benutzer akzeptiert hat - das verbessert diese Antwort nicht – Jake

Verwandte Themen