regex para que coincida, pero solo reemplace parte del patrón, en python

Aug 17 2020

Todos los ejemplos que he encontrado sobre el desbordamiento de la pila son demasiado complicados para que pueda realizar ingeniería inversa .

Considere este ejemplo de juguete

s = "asdfasd a_b dsfd"

quiero s = "asdfasd a'b dsfd"

Es decir: busque dos caracteres separados por un guión bajo y reemplace ese guión bajo con un apóstrofo

Intento:

re.sub("[a-z](_)[a-z]","'",s)
# "asdfasd ' dsfd"

Pensé que ()se suponía que iban a resolver este problema.

Aún más confuso es el hecho de que parece que encontramos con éxito el carácter que queremos reemplazar:

re.findall("[a-z](_)[a-z]",s)
#['_']

¿Por qué no se reemplaza esto?

Gracias

Respuestas

3 Marat Aug 17 2020 at 00:27

Utilice patrones de anticipación y retroceso:

re.sub("(?<=[a-z])_(?=[a-z])","'",s)

Los patrones de mirar adelante / atrás tienen un ancho cero y, por lo tanto, no reemplazan nada.

UPD:

  • El problema era que re.subreemplazaría toda la expresión coincidente, incluidas la letra anterior y la siguiente.
  • re.findalltodavía coincidía con la expresión completa, pero también tenía un grupo (el paréntesis dentro), que usted observó. Todo el partido estaba quietoa_b
  • Las expresiones lookahead / lookbehind comprueban que la búsqueda esté precedida / seguida de un patrón, pero no la incluya en la coincidencia.
  • otra opción era crear varios grupos y poner esos grupos en el reemplazo: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

Cuando se usa re.sub, el texto a conservar debe ser capturado, el texto a eliminar no.

Utilizar

re.sub(r"([a-z])_(?=[a-z])",r"\1'",s)

Ver prueba .

EXPLICACIÓN

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  _                        '_'
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of look-ahead

Código Python :

import re
s = "asdfasd a_b dsfd"
print(re.sub(r"([a-z])_(?=[a-z])",r"\1'",s))

Salida:

asdfasd a'b dsfd
ShinNShirley Sep 30 2020 at 14:33

El re.subsustituirá a todo lo que corresponde.

Existe una forma más general de resolver su problema y no necesita volver a modificar su expresión regular.

Código a continuación:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0)) if _reg else r

# 
re.sub(reg,repl, s)

salida: 'Data: year=am_replace_str, monthday=am_replace_str, month=am_replace_str, some other text'

Por supuesto, si su caso no contiene grupos, su código puede tener este aspecto:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0))

# 
re.sub(reg,repl, s)