Usando solo la expresión regular de python, ¿cómo encontrar y reemplazar la aparición de palabras en una oración? Por ejemplo:
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)
Tengo una oración arriba donde la palabra “gato” aparece dos veces en la oración. Quiero que la segunda aparición del ‘gato’ se cambie a ‘Bull’ dejando la primera palabra ‘gato’ sin tocar. Mi oración final se vería como: “gato ganso ratón caballo cerdo Bull vaca”. En mi código anterior intenté 3 veces diferentes no pude obtener lo que quería.
Utilice lookahead negativo como abajo.
>>> 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'
MANIFESTACIÓN
^
Afirma que estamos al principio. (?:(?!cat).)*
Coincide con cualquier carácter pero no con cat
, cero o más veces. cat
coincide con la primera subcadena de cat
. (?:(?!cat).)*
Coincide con cualquier carácter pero no con cat
, cero o más veces. ((?:(?!cat).)*cat(?:(?!cat).)*)
, Para que podamos referir esos caracteres capturados más adelante. cat
ahora la siguiente segunda cadena de cat
coincide. O
>>> s = "cat goose mouse horse pig cat cow" >>> re.sub(r'^(.*?(cat.*?){1})cat', r'\1Bull', s) 'cat goose mouse horse pig Bull cow'
Cambie el número dentro de {}
para reemplazar la primera o la segunda o la novena aparición de la cadena cat
Para reemplazar la tercera aparición de la cadena cat
, ponga 2
dentro de las llaves.
>>> 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'
Juega con la expresión regular anterior aquí …
Aquí hay una manera de hacerlo sin una expresión regular:
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)
Uso:
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
Uso una función simple, que enumera todas las apariciones, elige la posición de la nth 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 de nuevo 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.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
realidad es una lista de posiciones de coincidencias, donde se selecciona la enésima. Pero el índice del elemento de la lista comienza con0
generalmente, no con1
. Por lo tanto, hay un índicen-1
yn
variable es la subcadena nth real. Mi ejemplo encuentra la 5ta cuerda. Si usa el índicen
y quiere encontrar la quinta posición, necesitará quen
sea4
. Que usas usualmente depende de la función, la cual genera nuestron
.Esta debería ser la forma más sencilla, pero no es una expresión regular solo como originalmente quería.
Fuentes y algunos enlaces además:
where
construcción: encontrar todas las apariciones 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: Encuentre la enésima aparición de subcadenas en una cadena
Definiría una función que funcionará para cada expresión regular:
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:])
Un ejemplo de trabajo:
str = 'cat goose mouse horse pig cat cow' ns = replace_ith_instance(str, 'cat', 'Bull', 2) print(ns)
La salida:
cat goose mouse horse pig Bull cow
Otro ejemplo:
str2 = 'abc abc def abc abc' ns = replace_ith_instance(str2, 'abc\s*abc', '666') print(ns)
La salida:
abc abc def 666
Puede hacer coincidir las dos apariciones de “cat”, mantener todo antes de la segunda aparición ( \1
) y agregar “Bull”:
new_str = re.sub(r'(cat.*?)cat', r'\1Bull', str, 1)
Hacemos solo una sustitución para evitar reemplazar la cuarta, sexta, etc. ocurrencia de “cat” (cuando hay al menos cuatro ocurrencias), como lo señala el comentario de Avinash Raj.
Si desea reemplazar la n
-ésima ocurrencia y no la segunda, use:
n = 2 new_str = re.sub('(cat.*?){%d}' % (n - 1) + 'cat', r'\1Bull', str, 1)
Por cierto, no debe usar str
como nombre de variable ya que es una palabra clave reservada de Python.
Cree una función de re.sub()
para pasar a re.sub()
. Excepto que … el truco es convertirlo en una clase para que puedas hacer un seguimiento del número de llamadas.
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)
Entonces úsalo así:
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)
Estoy seguro de que hay una forma más inteligente de evitar el uso de una clase, pero esto parece lo suficientemente claro como para explicar. Además, asegúrese de devolver match.expand()
ya que solo devolver el valor de reemplazo no es técnicamente correcto o alguien decide usar plantillas de tipo \1
.
Cómo reemplazar la needle
nth
con la word
:
s.replace(needle,'$$$',n-1).replace(needle,word,1).replace('$$$',needle)