2011-11-04 17 views
21

¿Es esto realmente factible? Tengo algunas reglas de patrón de expresiones regulares muy largas que son difíciles de entender porque no caben en la pantalla de una vez. Ejemplo:Cómo dividir reglas largas de expresión regular en varias líneas en Python

test = re.compile('(?P<full_path>.+):\d+:\s+warning:\s+Member\s+(?P<member_name>.+)\s+\((?P<member_type>%s)\) of (class|group|namespace)\s+(?P<class_name>.+)\s+is not documented' % (self.__MEMBER_TYPES), re.IGNORECASE) 

Las comillas inversas o triples no funcionarán.

EDITAR. Terminé usando el VERBO más. Así es como se ve ahora el patrón de expresiones regulares:

test = re.compile(''' 
    (?P<full_path>         # Capture a group called full_path 
    .+           # It consists of one more characters of any type 
)            # Group ends      
    :            # A literal colon 
    \d+            # One or more numbers (line number) 
    :            # A literal colon 
    \s+warning:\s+parameters\sof\smember\s+   # An almost static string 
    (?P<member_name>        # Capture a group called member_name 
    [            # 
     ^:           # Match anything but a colon (so finding a colon ends group) 
    ]+           # Match one or more characters 
    )            # Group ends 
    (            # Start an unnamed group 
    ::           # Two literal colons 
    (?P<function_name>       # Start another group called function_name 
     \w+          #  It consists on one or more alphanumeric characters 
    )           # End group 
    )*            # This group is entirely optional and does not apply to C 
    \s+are\snot\s\(all\)\sdocumented''',   # And line ends with an almost static string 
    re.IGNORECASE|re.VERBOSE)      # Let's not worry about case, because it seems to differ between Doxygen versions 
+10

're.VERBOSE' [Ejemplo] (http: // stackoverflow .com/questions/7957846/python-regex-meaning/7958248 # 7958248) – jfs

+0

@JF Sebastian: ¡Tuve que dar +1 para re.DEBUG solo, eso hará mi vida mucho más fácil en el futuro! – Makis

+0

@ J.F.Sebastian: subí tu respuesta detrás del enlace porque al final todavía terminé usándolo aunque requirió más edición (tenía que asegurarte de que cada espacio en blanco esté marcado correctamente). – Makis

Respuesta

27

Puede dividir su patrón de expresiones regulares citando cada segmento. No se necesitan barras diagonales inversas.

test = re.compile(('(?P<full_path>.+):\d+:\s+warning:\s+Member' 
        '\s+(?P<member_name>.+)\s+\((?P<member_type>%s)\) ' 
        'of (class|group|namespace)\s+(?P<class_name>.+)' 
        '\s+is not documented') % (self.__MEMBER_TYPES), re.IGNORECASE) 

También puede utilizar la bandera cadena de texto en 'r' y tendrás que ponerlo antes de cada segmento.

Ver the docs.

17

De http://docs.python.org/reference/lexical_analysis.html#string-literal-concatenation:

Múltiples literales de cadena adyacente (delimitado por espacios en blanco), posiblemente utilizando diferentes convenciones citando, están permitidos, y su significado es la misma como su concatenación. Por lo tanto, "hola" 'mundo' es equivalente a para "helloworld". Esta característica se puede utilizar para reducir el número de barras invertidas necesario, para dividir cadenas largas convenientemente a través de largas líneas, o incluso añadir comentarios a las partes de las cadenas, por ejemplo:

re.compile("[A-Za-z_]"  # letter or underscore 
      "[A-Za-z0-9_]*" # letter, digit or underscore 
     ) 

Tenga en cuenta que esta característica se define en el nivel sintáctico, pero se implementó en tiempo de compilación. El operador '+' debe usarse para concatenar expresiones de cadena en tiempo de ejecución. También tenga en cuenta que la concatenación literal puede usar diferentes estilos de comillas para cada componente (incluso mezclando cadenas sin formato y cadenas entre comillas).

+1

+1 para cotizaciones detalladas de la documentación. –

0

El compilador de Python concatenará automáticamente los literales de cadena adyacentes. De modo que una forma de hacerlo es dividir su expresión regular en varias cadenas, una en cada línea, y dejar que el compilador de Python las recombine. No importa qué espacio en blanco tenga entre las cadenas, por lo que puede tener saltos de línea e incluso espacios iniciales para alinear los fragmentos de manera significativa.

0

O utilice la concatenación de cadena como en la respuesta de naeg o use re.VERBOSE/re.X, pero tenga cuidado esta opción ignorará el espacio en blanco y los comentarios. Usted tiene algunos espacios en su expresión regular, por lo que se ignorarían y necesita escaparse de ellos o usar \s

Así, por ejemplo,

test = re.compile("""(?P<full_path>.+):\d+: # some comment 
    \s+warning:\s+Member\s+(?P<member_name>.+) #another comment 
    \s+\((?P<member_type>%s)\)\ of\ (class|group|namespace)\s+ 
    (?P<class_name>.+)\s+is\ not\ documented""" % (self.__MEMBER_TYPES), re.IGNORECASE | re.X) 
+0

Intenté esto primero, pero no funcionó. Tal vez cometí algún error, pero mi pensamiento inicial fue que Python incluía el espacio en blanco. Al menos cuando imprimo algo con ese estilo, también se imprimen espacios en blanco. – Makis

4

En lo personal, yo no uso re.VERBOSE porque no me gusta para escapar de los espacios en blanco y no me quiero poner '\ s' en lugar de espacios en blanco cuando '\ s' no es obligatorio.
Cuantos más símbolos en un patrón de expresión regular son precisos en relación con las secuencias de caracteres que deben capturarse, más rápido actúa el objeto de expresión regular. Casi nunca uso '\ s'

.

Para evitar re.VERBOSE, puede hacerlo como lo ha sido ya dicho:

test = re.compile(
'(?P<full_path>.+)' 
':\d+:\s+warning:\s+Member\s+' # comment 
'(?P<member_name>.+)' 
'\s+\(' 
'(?P<member_type>%s)' # comment 
'\) of ' 
'(class|group|namespace)' 
#  ^^^^^^ underlining something to point out 
'\s+' 
'(?P<class_name>.+)' 
#  vvv overlining something important too 
'\s+is not documented'\ 
% (self.__MEMBER_TYPES), 

re.IGNORECASE) 

Empujar las cuerdas a la izquierda da un montón de espacio para escribir comentarios.

.

Pero de esta manera no es tan bueno cuando el patrón es muy larga, ya que no es posible escribir

test = re.compile(
'(?P<full_path>.+)' 
':\d+:\s+warning:\s+Member\s+' # comment 
'(?P<member_name>.+)' 
'\s+\(' 
'(?P<member_type>%s)' % (self.__MEMBER_TYPES) # !!!!!! INCORRECT SYNTAX !!!!!!! 
'\) of ' 
'(class|group|namespace)' 
#  ^^^^^^ underlining something to point out 
'\s+' 
'(?P<class_name>.+)' 
#  vvv overlining something important too 
'\s+is not documented', 

re.IGNORECASE) 

continuación, en caso de que el patrón es muy larga, el número de líneas entre el
parte % (self.__MEMBER_TYPES) al final
y la cadena '(?P<member_type>%s)' a la que se aplica
puede ser grande y perdemos la facilidad de leer el patrón.

.

Es por eso que me gusta usar una tupla en escribir una muy larga patrón:

pat = ''.join((
'(?P<full_path>.+)', 
# you can put a comment here, you see: a very very very long comment 
':\d+:\s+warning:\s+Member\s+', 
'(?P<member_name>.+)', 
'\s+\(', 
'(?P<member_type>%s)' % (self.__MEMBER_TYPES), # comment here 
'\) of ', 
# comment here 
'(class|group|namespace)', 
#  ^^^^^^ underlining something to point out 
'\s+', 
'(?P<class_name>.+)', 
#  vvv overlining something important too 
'\s+is not documented')) 

.

esta manera permite definir el patrón como una función:

def pat(x): 

    return ''.join((\ 
'(?P<full_path>.+)', 
# you can put a comment here, you see: a very very very long comment 
':\d+:\s+warning:\s+Member\s+', 
'(?P<member_name>.+)', 
'\s+\(', 
'(?P<member_type>%s)' % x , # comment here 
'\) of ', 
# comment here 
'(class|group|namespace)', 
#  ^^^^^^ underlining something to point out 
'\s+', 
'(?P<class_name>.+)', 
#  vvv overlining something important too 
'\s+is not documented')) 

test = re.compile(pat(self.__MEMBER_TYPES), re.IGNORECASE) 
5

simplemente para la corrección, la falta de respuesta aquí es el uso de la bandera re.X o re.VERBOSE, que el PO, finalmente, señaló. Además de guardar las comillas, este método también es portátil en otras implementaciones de expresiones regulares como Perl.

De https://docs.python.org/2/library/re.html#re.X:

re.X 
re.VERBOSE 

Este indicador permite escribir expresiones regulares que se ven mejor y son más legibles por lo que le permite secciones lógicas de separar visualmente el patrón y añadir comentarios. El espacio en blanco dentro del patrón se ignora, excepto cuando está en una clase de caracteres o cuando está precedido por una barra invertida no guardada. Cuando una línea contiene un # que no está en una clase de caracteres y no está precedida por una barra invertida no guardada, se ignoran todos los caracteres de la parte más a la izquierda como # hasta el final de la línea.

Esto significa que los dos siguientes objetos de expresiones regulares que coincidan con un número decimal son funcionalmente iguales:

a = re.compile(r"""\d + # the integral part 
        \. # the decimal point 
        \d * # some fractional digits""", re.X) 

 

b = re.compile(r"\d+\.\d*") 
Cuestiones relacionadas