python - Reemplazar enésima aparición de subcadena en cadena
string replace (7)
He modificado la respuesta de @ aleskva para trabajar mejor con expresiones regulares y comodines:
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)
Esto le da a
abdsahd15jhdasjk124ndjaksnd124ndjkas
.
Tenga en cuenta el uso de
?
hacer que la consulta no sea codiciosa.
Me doy cuenta de que la pregunta establece explícitamente que no querían usar expresiones regulares, sin embargo, puede ser útil poder usar comodines de manera clara (de ahí mi respuesta).
Quiero reemplazar la enésima aparición de una subcadena en una cadena.
Tiene que haber algo equivalente a lo que QUIERO hacer, que es
mystring.replace("substring", 2nd)
¿Cuál es la forma más simple y más pitónica de lograr esto?
¿Por qué no duplicar ? No quiero usar expresiones regulares para este enfoque y la mayoría de las respuestas a preguntas similares que encontré son solo la eliminación de expresiones regulares o una función realmente compleja. Realmente quiero una solución lo más simple posible y no regex.
La última respuesta es casi perfecta, solo una corrección:
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
La cadena posterior debe almacenarse en esta variable nuevamente después del reemplazo. ¡Gracias por la gran solución!
Puede usar un ciclo while con
str.find
para encontrar la enésima ocurrencia si existe y usar esa posición para crear la nueva cadena:
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
Ejemplo:
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''
Se me ocurrió lo siguiente, que considera también las opciones para reemplazar todas las ocurrencias de cadenas ''antiguas'' a la izquierda o a la derecha. Naturalmente, no hay ninguna opción para reemplazar todas las ocurrencias, ya que el reemplazo estándar de str funciona perfectamente.
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)
Tenía una necesidad similar, es decir, encontrar las IP en los registros y reemplazar solo el campo src IP o dst IP selectivamente. Así es como lo logré de manera pitónica;
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)
Utilizo una función simple, que enumera todas las ocurrencias, selecciona la enésima posición y la usa para dividir la cadena original en dos subcadenas. Luego reemplaza la primera aparición en la segunda subcadena y une las subcadenas nuevamente en la nueva cadena:
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
Para estas variables:
string = ''ababababababababab''
sub = ''ab''
wanted = ''CD''
n = 5
salidas:
ababababCDabababab
Notas:
La variable
where
es en realidad una lista de posiciones de coincidencias, donde se selecciona la enésima. Pero el índice de elementos de lista generalmente comienza con0
, no con1
. Por lo tanto, hay un índicen-1
n
variable es la enésima subcadena real. Mi ejemplo encuentra la quinta cadena. Si usan
index y desea encontrar la quinta posición, necesitará quen
sea4
. El que usas generalmente depende de la función, que genera nuestron
.Esta debería ser la forma más simple, pero tal vez no sea la forma más pitónica, porque la construcción de variables necesita importar
re
biblioteca. Quizás alguien encuentre aún más forma pitónica.Fuentes y algunos enlaces además:
where
construcción: Encuentra todas las ocurrencias de una subcadena en Python- división de cadenas: https://www.daniweb.com/programming/software-development/threads/452362/replace-nth-occurrence-of-any-sub-string-in-a-string
- pregunta similar: encontrar la enésima aparición de subcadena en una cadena
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