2011-02-05 18 views
9

La forma en que la expresión regular funciona en Python es tan intensamente desconcertante que me pone más furioso con cada segundo que pasa. Aquí está mi problema:¿Por qué mi expresión regular con r'string 'coincide pero no' cadena 'con Python?

entiendo que esto da resultado:

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

mientras que esto no es así:

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

Y eso está bien. Lo entiendo mucho. Ahora, tengo una expresión regular que se está generando de esta manera:

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

Si ahora hago re.search(regex, 'grand rapids, mi 49505'), falla por la misma razón mi segundo ejemplo search() falla.

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

+3

Nota: Los corchetes son redundantes (en realidad, dañino: la diferencia entre 'O (n)' y 'O (1)' consumo de memoria), use una [expresión del generador] (http://docs.python.org/tutorial/classes.html#generator-expressions) en su lugar. – delnan

Respuesta

14

El anwser sí

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

La razón detrás de esto es que la El prefijo 'r' le dice a Python que no analice la cadena que le pasa. Si no pone una 'r' antes de la cadena, Python intentará convertir cualquier carácter precedente por '\' en un carácter especial, para permitirle ingresar líneas de salto (\ n), pestañas (\ t) y tal fácilmente.

Al hacer '\b', le dice a Python para crear una cadena, analizarla, y transformar '\ b' en 'retroceso', mientras que cuando lo hace r'\b', Python simplemente tienda '\' después 'b', y esto es lo que quieres para regex. Utilice siempre 'r' para la cadena utilizada como patrones de expresiones regulares.

La notación 'r' se llama 'cadena sin procesar', pero eso es engañoso, ya que no existe una cadena sin formato en las partes internas de Python. Solo piénselo como una forma de decirle a Python que evite ser demasiado inteligente.

Hay otra anotació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 maneras de mejorar su 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 su join() y en ninguna otra parte.

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

Esto se encargará de la conversión de cadenas automáticamente y es más corto y más limpio.Cuando formatee una cadena en Python, piense en el % operator.

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

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

Con el tiempo, puede que no necesite de expresiones regulares en absoluto. Si todo lo que importa es comprobar si uno del código postal está en la cadena dada, sólo puede utilizar in (comprobar si un artículo está en un iterable, como si una cadena está en una lista):

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

Última palabra

entiendo que puede ser frustrado cuando el aprendizaje de 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.

+0

Cómo sobre una explicación? – delnan

+1

Tu francés se está mostrando. Liste> lista. – ash

+2

Maldición. Espera, podría ser italiano! –

4

La solución es la que utilizó usted mismo en el ejemplo anterior: cadenas sin formato.

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

(Tenga en cuenta que también me quita los soportes adicionales, que convierten la comprensión de lista en una expresión generador.)

+0

¿Por qué la necesidad de cadenas sin procesar? – cledoux

+1

Just '\ b' en una cadena se interpreta como una secuencia de escape y produce un código de control BELL (creo). Sin embargo, cuando desee que la expresión regular (resultante) incluya una secuencia de escape, deberá asegurarse de que toda la secuencia esté incluida en la cadena. Por lo tanto, debe escapar de la barra diagonal inversa por separado: '\\ b' que da como resultado' \ b' dentro de la cadena. – poke

+0

@ char8705: agregué una razón para eso en mi awser. –

2

La clave es entender la diferencia entre '\ b' y r '\ b'. Al escribir estos en los resultados de ralentí en esta salida:

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

Así que cada vez que escriba en una barra invertida en una expresión regular, que debe escapar de ella mediante el uso de la notación de cadena de texto.

+0

Esto es engañoso. Python no almacena r '\ b' como '\\ b' internamente. Es IDLE y la mayoría del shell Python que lo muestra de esa manera para darle una pista de lo que hay adentro. Pero no obtendrás lo mismo si imprimes. Esto va a confundir a todos los novatos. –

0

Vamos a romper estas dos cadenas abajo:

r'\bmi\b' 

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

re interpreta los dos personajes \ y b como una ruptura palabra.

'\bmi\b' 

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

lo tanto la construcción de abajo está buscando retrocesos, no separación de palabras:

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

probar este (dejando caer str, el estado ya debería ser una cadena):

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

La palabra descanso doesn' Necesito ser procesado en cada expresión OR.Tirando de él simplifica el Ingreso:

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

Desde Pythonistas generalmente ceño en expresiones regulares, como bien podría hacer una lectura uno:

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) 
+0

"Dado que los Pythonistas usualmente fruncen el ceño a las expresiones regulares ..." Interesante. ¿Cómo? ¿Qué usarías en su lugar? –

Cuestiones relacionadas