¿Cómo encontrar y reemplazar la aparición de palabras en una oración usando la expresión regular de python?

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.
  • Ahora, encierre todos los patrones dentro de un grupo de captura como ((?:(?!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 con 0 generalmente, no con 1 . Por lo tanto, hay un índice n-1 y n variable es la subcadena nth real. Mi ejemplo encuentra la 5ta cuerda. Si usa el índice n y quiere encontrar la quinta posición, necesitará que n sea 4 . Que usas usualmente depende de la función, la cual genera nuestro n .

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:

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)