2012-02-03 16 views
27

Para hacer un cambio de expresiones regulares, hay tres cosas que darle:Regex en python: ¿es posible obtener la coincidencia, el reemplazo y la cadena final?

  • El perfil de compatibilidad
  • El patrón de sustitución
  • La cadena original

Hay tres cosas que las el motor regex encuentra que me interesan:

  • La coincidencia cadena
  • La sustitución cadena
  • La cadena final procesado

Al utilizar re.sub, la cadena final es lo que se devuelve. ¿Pero es posible acceder a las otras dos cosas, la cadena combinada y la cadena de reemplazo?

He aquí un ejemplo:

orig = "This is the original string." 
matchpat = "(orig.*?l)" 
replacepat = "not the \\1" 

final = re.sub(matchpat, replacepat, orig) 
print(final) 
# This is the not the original string 

La cadena de búsqueda es "original" y la cadena de reemplazo es "not the original". ¿Hay alguna manera de obtenerlos? Estoy escribiendo una secuencia de comandos para buscar y reemplazar en muchos archivos, y quiero que imprima lo que está buscando y reemplazando, sin imprimir toda la línea.

+0

Me he estado preguntando esto por un tiempo también. ¡Buena pregunta! – Blender

+0

Muy buena pregunta. Y muy bien formulado. +1 – ovgolovin

Respuesta

26
class Replacement(object): 

    def __init__(self, replacement): 
     self.replacement = replacement 
     self.matched = None 
     self.replaced = None 

    def __call__(self, match): 
     self.matched = match.group(0) 
     self.replaced = match.expand(self.replacement) 
     return self.replaced 

>>> repl = Replacement('not the \\1') 
>>> re.sub('(orig.*?l)', repl, 'This is the original string.') 
    'This is the not the original string.' 
>>> repl.matched 
    'original' 
>>> repl.replaced 
    'not the original' 

Editar: como @FJ tiene señalado, lo anterior solo recordará el último partido/reemplazo. Esta versión maneja múltiples ocurrencias:

class Replacement(object): 

    def __init__(self, replacement): 
     self.replacement = replacement 
     self.occurrences = [] 

    def __call__(self, match): 
     matched = match.group(0) 
     replaced = match.expand(self.replacement) 
     self.occurrences.append((matched, replaced)) 
     return replaced 

>>> repl = Replacement('[\\1]') 
>>> re.sub('\s(\d)', repl, '1 2 3') 
    '1[2][3]' 

>>> for matched, replaced in repl.occurrences: 
    ....:  print matched, '=>', replaced 
    ....:  
2 => [2] 
3 => [3] 
+1

+1: Mucho mejor que mi solución. Robaré este código. – Blender

+0

Esto solo almacenaría la última coincidencia y el reemplazo, pero no sería difícil convertir las variables de instancia "emparejado" y "reemplazado" en listas para conservarlas todas. –

+1

+1 para pasar una clase invocable donde las personas normalmente usan funciones. Inteligente. – twneale

9

Miré a la documentación y parece que puede pasar una referencia a una función en el re.sub:

import re 

def re_sub_verbose(pattern, replace, string): 
    def substitute(match): 
    print 'Matched:', match.group(0) 
    print 'Replacing with:', match.expand(replace) 

    return match.expand(replace) 

    result = re.sub(pattern, substitute, string) 
    print 'Final string:', result 

    return result 

Y consigo esta salida cuando se ejecuta re_sub_verbose("(orig.*?l)", "not the \\1", "This is the original string."):

Matched: original 
Replacing with: not the original 
This is the not the original string. 
+1

+1, estaba en el medio de escribir lo mismo –

+0

Simplemente no puedo entender cómo hacerlo bonito para que funcione para otras expresiones regulares, ya que estoy usando esto en mi propio código: P – Blender

+2

También puede llamar a 'match.expand ('not the \\ 1')' (http://docs.python.org/library/re.html#re.MatchObject.expand) –

Cuestiones relacionadas