web-dev-qa-db-fra.com

Remplacer la nième occurrence de sous-chaîne dans la chaîne

Je veux remplacer la nième occurrence d'une sous-chaîne dans une chaîne.

Il doit y avoir quelque chose d'équivalent à ce que je veux faire qui est 

mystring.replace("substring", 2nd)

Quel est le moyen le plus simple et le plus pythonique d’atteindre cet objectif?

Pourquoi ne pas dupliquer: Je ne veux pas utiliser regex pour cette approche et la plupart des réponses à des questions similaires que j'ai trouvées ne sont que du stripping de regex ou une fonction très complexe. Je veux vraiment une solution aussi simple que possible et non regex.

11
aleskva

J'utilise une fonction simple, qui répertorie toutes les occurrences, sélectionne la nième position et l'utilise pour scinder la chaîne d'origine en deux sous-chaînes. Ensuite, il remplace la première occurrence dans la deuxième sous-chaîne et joint les sous-chaînes dans la nouvelle chaîne:

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

Pour ces variables:

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

les sorties:

ababababCDabababab

Remarques:

La variable where est en fait une liste des positions des correspondances, où vous choisissez la nième. Mais l'index des éléments de liste commence par 0 en général, pas par 1. Par conséquent, il existe un index n-1 et la variable n est la nième sous-chaîne actuelle. Mon exemple trouve la 5ème chaîne. Si vous utilisez n index et que vous voulez trouver la 5ème position, vous aurez besoin de n pour être 4. Ce que vous utilisez dépend généralement de la fonction qui génère notre n.

Cela devrait être la méthode la plus simple, mais peut-être pas la méthode la plus pythonique, car la construction de la variable where nécessite d'importer la bibliothèque re. Peut-être que quelqu'un trouvera encore plus de manière pythonique.

Sources et quelques liens en plus:

2
aleskva

Vous pouvez utiliser une boucle while avec str.find pour rechercher la nième occurrence si elle existe et utiliser cette position pour créer la nouvelle chaîne:

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

Exemple:

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
Padraic Cunningham

Je suis venu avec ce qui suit, qui considère également les options pour remplacer toutes les "anciennes" occurrences de chaîne à gauche ou à droite. Naturellement, il n'y a pas d'option pour remplacer toutes les occurrences, car str.replace standard fonctionne parfaitement.

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

La dernière réponse est presque parfaite - une seule correction:

    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

L'après-chaîne doit être à nouveau stockée dans la variable après son remplacement. Merci pour cette excellente solution!

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

J’avais le même besoin, c’est-à-dire de trouver les adresses IP dans les journaux et de ne remplacer que le champ src IP ou dst IP. C’est ainsi que j’ai réalisé de manière pythonique;

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