web-dev-qa-db-de.com

Ersetzen Sie das n-te Vorkommen der Teilzeichenfolge in der Zeichenfolge

Ich möchte das n-te Vorkommen eines Teilstrings in einem String ersetzen.

Es muss etwas Äquivalentes zu dem geben, was ich tun möchte 

mystring.replace("substring", 2nd)

Was ist der einfachste und pythonischste Weg, um dies zu erreichen?

Warum nicht duplizieren: Ich möchte Regex nicht für diesen Ansatz verwenden, und die meisten Antworten auf ähnliche Fragen, die ich gefunden habe, sind nur Regex-Stripping oder eine wirklich komplexe Funktion. Ich möchte wirklich so einfach wie möglich und keine Regex-Lösung.

11
aleskva

Ich verwende eine einfache Funktion, die alle Vorkommen auflistet, die n-te Position des Benutzers auswählt und die ursprüngliche Zeichenfolge in zwei Teilstrings aufteilt. Dann ersetzt es das erste Vorkommen in der zweiten Teilzeichenfolge und fügt die Teilzeichenfolgen wieder in die neue Zeichenfolge ein:

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

Anmerkungen:

Die Variable whereist tatsächlich eine Liste der Übereinstimmungspositionen, an der Sie die n-te Position abrufen. Der Listenelementindex beginnt jedoch normalerweise mit 0, nicht mit 1. Daher gibt es einen n-1-Index und nVariable ist der eigentliche n-te Teilstring. Mein Beispiel findet die 5. Saite. Wenn Sie nindex verwenden und die fünfte Stelle suchen möchten, müssen Sie nals 4 angeben. Was Sie verwenden, hängt in der Regel von der Funktion ab, die unsere ngeneriert.

Dies sollte der einfachste Weg sein, aber es ist vielleicht nicht der pythonischste Weg, da für die Konstruktion der Variablen wheredie Bibliothek reimportiert werden muss. Vielleicht findet jemand noch mehr Pythonic-Weg.

Quellen und einige Links dazu:

2
aleskva

Sie können eine while-Schleife mit str.find verwenden, um das n-te Vorkommen zu finden, falls vorhanden, und diese Position zum Erstellen der neuen Zeichenfolge verwenden:

def nth_repl(s, sub, repl, nth):
    find = s.find(sub)
    # if find is not p1 we have found at least one match for the substring
    i = find != -1
    # loop util we find the nth or we find no match
    while find != -1 and i != nth:
        # find + 1 means we start at the last match start index + 1
        find = s.find(sub, find + 1)
        i += 1
    # if i  is equal to nth we found nth matches so replace
    if i == nth:
        return s[:find]+repl+s[find + len(sub):]
    return s

Beispiel:

In [14]: s = "foobarfoofoobarbar"

In [15]: nth_repl(s, "bar","replaced",3)
Out[15]: 'foobarfoofoobarreplaced'

In [16]: nth_repl(s, "foo","replaced",3)
Out[16]: 'foobarfooreplacedbarbar'

In [17]: nth_repl(s, "foo","replaced",5)
Out[17]: 'foobarfoofoobarbar'
11

Ich habe mir das folgende vorgestellt, das auch Optionen in Betracht zieht, um alle 'alten' String-Vorkommen nach links oder nach rechts zu ersetzen. Natürlich gibt es keine Option, um alle Vorkommen zu ersetzen, da str.replace standardmäßig perfekt funktioniert.

def nth_replace(string, old, new, n=1, option='only nth'):
    """
    This function replaces occurrences of string 'old' with string 'new'.
    There are three types of replacement of string 'old':
    1) 'only nth' replaces only nth occurrence (default).
    2) 'all left' replaces nth occurrence and all occurrences to the left.
    3) 'all right' replaces nth occurrence and all occurrences to the right.
    """
    if option == 'only nth':
        left_join = old
        right_join = old
    Elif option == 'all left':
        left_join = new
        right_join = old
    Elif option == 'all right':
        left_join = old
        right_join = new
    else:
        print("Invalid option. Please choose from: 'only nth' (default), 'all left' or 'all right'")
        return None
    groups = string.split(old)
    nth_split = [left_join.join(groups[:n]), right_join.join(groups[n:])]
    return new.join(nth_split)
2
CapedHero

Die letzte Antwort ist nahezu perfekt - nur eine Korrektur:

    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 = after.replace(sub, wanted)
        newString = before + after
        return newString

Die Nachfolge muss nach dem Ersetzen wieder in dieser Variablen gespeichert werden. Vielen Dank für die großartige Lösung!

0
Victor.Bbb
def replace_nth_occurance(some_str, original, replacement, n):
    """ Replace nth occurance of a string with another string
    """
    some_str.replace(original, replacement, n)
    for i in range(n):
        some_str.replace(replacement, original, i)
    return some_str
0
vineeshvs

Ich hatte ein ähnliches Bedürfnis, d. H. Die IPs in Protokollen zu finden und nur src IP oder dst IP Feld selektiv zu ersetzen. So habe ich Pythonic erreicht;

import re

mystr = '203.23.48.0 DENIED 302 449 800 1.1 302 http d.flashresultats.fr  10.111.103.202 GET GET - 188.92.40.78 '
src = '1.1.1.1'
replace_nth = lambda mystr, pattern, sub, n: re.sub(re.findall(pattern, mystr)[n - 1], sub, mystr)
result = replace_nth(mystr, '\S*\d+\.\d+\.\d+\.\d+\S*', src, 2)
print(result)
0
user1387901

Ich habe die Antwort von @aleskva überarbeitet, um besser mit Regex und Wildcards arbeiten zu können:

import re

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

    return newString

replacenth('abdsahd124njhdasjk124ndjaksnd124ndjkas', '1.*?n', '15', 1)

Das gibt abdsahd15jhdasjk124ndjaksnd124ndjkas. Beachten Sie die Verwendung von ?, um die Abfrage nicht gierig zu machen.

Mir ist klar, dass die Frage ausdrücklich besagt, dass sie Regex nicht verwenden wollten, aber es kann nützlich sein, Wildcards in klarer Form zu verwenden (daher meine Antwort).

0
J.Warren