¿Por qué mi expresión regular con r’string ‘coincide pero no con’ string ‘usando Python?

La forma en que Regex funciona en Python es tan intrigante que me pone más furioso con cada segundo que pasa. Aquí está mi problema:

Entiendo que esto da un resultado:

re.search(r'\bmi\b', 'grand rapids, mi 49505) 

mientras que esto no lo hace:

 re.search('\bmi\b', 'grand rapids, mi 49505) 

Y eso está bien. Tengo mucho de eso. Ahora, tengo una expresión regular que se está generando así:

 regex = '|'.join(['\b' + str(state) + '\b' for state in states]) 

Si ahora hago re.search(regex, 'grand rapids, mi 49505') , falla por el mismo motivo por el que falla el segundo ejemplo de search() .

Mi pregunta: ¿Hay alguna manera de hacer lo que estoy tratando de hacer?

La propia respuesta

 regex = '|'.join([r'\b' + str(state) + r'\b' for state in states]) 

La razón detrás de esto es que el prefijo ‘r’ le dice a Python que no analice la cadena que le pasas. Si no pone una ‘r’ antes de la cadena, Python intentará convertir cualquier char precedente por ‘\’ en una char especial, para permitirle ingresar líneas de ruptura (\ n), tabulaciones (\ t) y demás. fácilmente.

Cuando haces '\b' , le dices a Python que cree una cadena, la analiza y transforma ‘\ b’ en ‘retroceso’, mientras que cuando haces r'\b' , Python simplemente almacena ‘\’ y luego ‘b’ , y esto es lo que quieres con regex. Siempre use ‘r’ para la cadena usada como patrones de expresiones regulares.

La notación ‘r’ se llama ‘cadena sin formato’, pero eso es engañoso, ya que no existe una cadena en bruto en los componentes internos de Python. Solo piénsalo como una manera de decirle a Python que evite ser demasiado inteligente.

Hay otra notación en Python <3.0, u’string ‘, que le dice a Python que almacene la cadena como unicode. Puede combinar ambos: ur"é\n" almacenará “\ bé” como unicode, mientras que u"é\n" almacenará “é” y luego un salto de línea.

Algunas formas de mejorar tu código:

 regex = '|'.join(r'\b' + str(state) + r'\b' for state in states) 

Eliminado el extra [] . Le dice a Python que no almacene en la memoria la lista de valores que está generando. Podemos hacerlo aquí porque no planeamos reutilizar la lista que está creando ya que la usa directamente en join() y en ninguna otra parte.

 regex = '|'.join(r'\b%s\b' % state for state in states) 

Esto se hará cargo de la conversión de la cadena automáticamente y es más corto y más limpio. Cuando formatees una cadena en Python, piensa en el operador% .

Si los estados contienen una lista de códigos postales de estados, entonces debe almacenarse como cadena, no como int. En ese caso, puede omitir el tipo de conversión y acortarlo aún más:

 regex = r'\b%s\b' % r'\b|\b'.join(states) 

Eventualmente, es posible que no necesite expresiones regulares en absoluto. Si todo lo que te importa es verificar si uno de los códigos postales está en la cadena dada, solo puedes usar in (verificar si un elemento está en una iterable, como si una cadena está en una lista):

 matches = [s for s in states if s in 'grand rapids, mi 49505'] 

Ultima palabra

Entiendo que puede sentirse frustrado al aprender un nuevo idioma, pero tómese el tiempo para dar un título adecuado a su pregunta. En este sitio web, el título debe terminar con un signo de interrogación y dar detalles específicos sobre el problema.

La solución es la que usaste en el ejemplo anterior: cadenas en bruto.

 regex = '|'.join(r'\b' + str(state) + r'\b' for state in states) 

(Tenga en cuenta que también eliminé los corchetes adicionales, convirtiendo la comprensión de la lista en una expresión generadora).

La clave es entender la diferencia entre ‘\ b’ y r ‘\ b’. Escribir estos resultados en IDLE en esta salida:

 >>> '\b' '\x08' >>> r'\b' '\\b' 

Por lo tanto, cada vez que escriba una barra invertida en una expresión regular, debe escapar utilizando la notación de cadena sin formato.

Vamos a romper estas dos cadenas hacia abajo:

 r'\bmi\b' 

Python interpreta la cadena anterior con seis caracteres de longitud (barra invertida, letra B, etc.). Una cadena en bruto suprime la traducción de Python de \ b en un retroceso.

re interpreta los dos caracteres \ y b como un salto de palabra.

 '\bmi\b' 

Python interpreta la cadena anterior con cuatro caracteres de longitud (retroceso, letra B, etc.).
Ahora re no ve nada especial que interpretar y busca esos cuatro caracteres literales.

Por lo tanto, la siguiente construcción está buscando backspaces, no saltos de palabras:

 regex = '|'.join(['\b' + str(state) + '\b' for state in states]) 

Intenta esto (soltar str , el estado ya debería ser una cadena):

 regex = '|'.join([r'\b' + state + r'\b' for state in states]) 

La palabra break no necesita ser procesada en cada expresión OR. Sacarlo simplifica la unión:

 regex = r'\b(' + '|'.join(states) + r')\b' 

Ya que los pitonistas suelen fruncir el ceño en expresiones regulares, también podrían ser de fácil lectura:

 import re pattern = re.compile(r''' (?ix) # ignore case, verbose \b # word break ( # begin group 1 AL|AK|AZ|AR|CA|CO|CT|DE|FL|GA| HI|ID|IL|IN|IA|KS|KY|LA|ME|MD| MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ| NM|NY|NC|ND|OH|OK|OR|PA|RI|SC| SD|TN|TX|UT|VT|VA|WA|WV|WI|WY ) # end group 1 \b # word break ''') m = pattern.search('Grand Rapids, MI 49505') if m: print m.group(1)