2011-03-27 19 views
7

Dado un archivo de texto, donde el carácter que quiero hacer coincidir está delimitado por comillas simples, pero podría tener cero o un único escape cita, así como cero o más pestañas y caracteres de nueva línea (no escapó) - Quiero hacer coincidir solo el texto. Ejemplo:Python regex para hacer coincidir el texto entre comillas simples, ignorando las comillas escapadas (y pestañas/líneas nuevas)

menu_item = 'casserole'; 
menu_item = 'meat 
      loaf'; 
menu_item = 'Tony\'s magic pizza'; 
menu_item = 'hamburger'; 
menu_item = 'Dave\'s famous pizza'; 
menu_item = 'Dave\'s lesser-known 
    gyro'; 

Quiero agarrar sólo el texto (y espacios), haciendo caso omiso de las pestañas/saltos de línea - y que en realidad no importa si la cita escapado aparece en los resultados, siempre y cuando no lo hace afectará al partido:

casserole 
meat loaf 
Tonys magic pizza 
hamburger 
Daves famous pizza 
Dave\'s lesser-known gyro # quote is okay if necessary. 

tengo gestionar para crear una expresión regular que casi lo hacen - que maneja las cotizaciones escapado, pero no los saltos de línea:

menuPat = r"menu_item = \'(.*)(\\\')?(\t|\n)*(.*)\'" 
for line in inFP.readlines(): 
    m = re.search(menuPat, line) 
    if m is not None: 
     print m.group() 

Definitivamente hay un montón de preguntas sobre expresiones regulares, pero la mayoría usa Perl, y si hay una que hace lo que quiero, no pude entenderlo :) Y como estoy usando Python, no lo hago cuidado si se extiende a través de múltiples grupos, es fácil recombinarlos.

Algunas respuestas han dicho simplemente ir con el código para analizar el texto. Aunque estoy seguro de que podría hacer eso - estoy tan cerrar para tener una expresión regular activa :) Y parece que debería ser factible.

Actualización: Me acabo de dar cuenta de que estoy haciendo un Python readlines() para obtener cada línea, lo que obviamente está dividiendo las líneas que pasan a la expresión regular. Estoy buscando volver a escribirlo, pero cualquier sugerencia sobre esa parte también sería muy útil.

+0

posible duplicado de [expresiones regulares para la gestión de caracteres de escape para elementos como literales de cadenas] (http://stackoverflow.com/questions/430759/regex-for-managing-escaped-characters-for-items-like-string-literals) – phooji

+0

No está duplicado, estoy tratando de manejar (nuevas escapadas) rompiendo mis datos de entrada, también. –

+1

Estoy de acuerdo, pero pensé que valía la pena señalar de todos modos. Simplemente use 're.MULTILINE' (http://docs.python.org/library/re.html#re.MULTILINE) para unir líneas múltiples,' $ 'para ignorar/unir líneas de finalización, y' \ s' (mismo enlace) para que coincida con el espacio de noticias. Adelante, saltamontes;) – phooji

Respuesta

3

Esto debe hacerlo:

menu_item = '((?:[^'\\]|\\')*)' 

Aquí la parte (?:[^'\\]|\\')* coincide con cualquier secuencia de cualquier carácter excepto ' y \ o un literal \'. La expresión anterior [^'\\] también permite saltos de línea y tabuladores que luego debe reemplazar por un solo espacio.

+0

Cuando dices "reemplazar por un espacio simple", ¿te refieres a limpiar/eliminar las pestañas/saltos de línea * antes * de ejecutar este RE? Cuando probé su RE, no coincidía con ninguna de las líneas con saltos. –

+0

@John C: No, lo haría después con algo como 're.sub (r" [\ n \ r \ t] + "," ", match)'. – Gumbo

+0

Sin embargo, mi variable de coincidencia * m *, está vacía para las líneas de entrada que tienen saltos de línea, por lo que no hay nada que sustituir. –

1

frías intenta de esta manera:

pattern = re.compile(r"menu_item = '(.*?)(?<!\\)'", re.DOTALL) 

que comenzará a juego en la primera comilla simple que encuentra y que termina en la primera comilla simple no precedido por una barra invertida. También captura las nuevas líneas y pestañas que se encuentran entre las dos comillas simples.

+0

Parece interesante, pero como noté en otro comentario, me acabo de dar cuenta de que estoy haciendo * readlines() *, que está rompiendo líneas nuevas, así que tengo otro problema que corregir. –

12

Este script probado debe hacer el truco:

import re 
re_sq_long = r""" 
    # Match single quoted string with escaped stuff. 
    '   # Opening literal quote 
    (   # $1: Capture string contents 
     [^'\\]* # Zero or more non-', non-backslash 
     (?:  # "unroll-the-loop"! 
     \\.  # Allow escaped anything. 
     [^'\\]* # Zero or more non-', non-backslash 
    )*   # Finish {(special normal*)*} construct. 
    )   # End $1: String contents. 
    '   # Closing literal quote 
    """ 
re_sq_short = r"'([^'\\]*(?:\\.[^'\\]*)*)'" 

data = r''' 
     menu_item = 'casserole'; 
     menu_item = 'meat 
        loaf'; 
     menu_item = 'Tony\'s magic pizza'; 
     menu_item = 'hamburger'; 
     menu_item = 'Dave\'s famous pizza'; 
     menu_item = 'Dave\'s lesser-known 
      gyro';''' 
matches = re.findall(re_sq_long, data, re.DOTALL | re.VERBOSE) 
menu_items = [] 
for match in matches: 
    match = re.sub('\s+', ' ', match) # Clean whitespace 
    match = re.sub(r'\\', '', match) # remove escapes 
    menu_items.append(match)   # Add to menu list 

print (menu_items) 

Aquí es la versión corta de la expresión regular:

'([^'\\]*(?:\\.[^'\\]*)*)'

Esta expresión regular se optimiza el uso de Jeffrey Friedl "desenrollar-the- loop " técnica de eficiencia. (Ver: Mastering Regular Expressions (3rd Edition)) para más detalles.

Tenga en cuenta que la expresión regular anterior es equivalente a la siguiente (que es más comúnmente visto, pero es mucho más lento en la mayoría de las implementaciones de expresiones regulares NFA):

'((?:[^'\\]|\\.)*)'

Cuestiones relacionadas