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.
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 par0
en général, pas par1
. Par conséquent, il existe un indexn-1
et la variablen
est la nième sous-chaîne actuelle. Mon exemple trouve la 5ème chaîne. Si vous utilisezn
index et que vous voulez trouver la 5ème position, vous aurez besoin den
pour être4
. Ce que vous utilisez dépend généralement de la fonction qui génère notren
.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èquere
. Peut-être que quelqu'un trouvera encore plus de manière pythonique.Sources et quelques liens en plus:
where
construction: Trouver toutes les occurrences d'une sous-chaîne en Python- division de chaîne: https://www.daniweb.com/programming/software-development/threads/452362/replace-nth-occurrence-of-any-sub-string-in-a-string
- question similaire: Trouver la nième occurrence de sous-chaîne dans une chaîne
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'
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)
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!
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
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)