Análisis de un archivo de texto estructurado en Python (pyparsing)

Por razones que realmente no entiendo, una API REST que estoy usando, en lugar de generar JSON o XML, usa un formato de texto estructurado peculiar. En su forma más simple

SECTION_NAME    entry  other qualifying bits of the entry
                entry2 other qualifying bits
                ...

No están delimitados por tabuladores, como puede parecer la estructura, sino delimitados por espacios, y los bits de calificación pueden contener palabras con espacios. El espacio entre SECTION_NAME y las entradas también es variable, desde 1 hasta varios (6 o más) espacios.

Además, una parte del formato contiene entradas en la forma

SECTION_NAME entry
  SUB_SECTION more information
  SUB_SECTION2 more information

Como referencia, un extracto de datos reales (se omiten algunas secciones), que muestra el uso de la estructura:

ENTRY       hsa04064                    Pathway
NAME        NF-kappa B signaling pathway - Homo sapiens (human)
DRUG        D09347  Fostamatinib (USAN)
            D09348  Fostamatinib disodium (USAN)
            D09692  Veliparib (USAN/INN)
            D09730  Olaparib (JAN/INN)
            D09913  Iniparib (USAN/INN)
REFERENCE   PMID:21772278
  AUTHORS   Oeckinghaus A, Hayden MS, Ghosh S
  TITLE     Crosstalk in NF-kappaB signaling pathways.
  JOURNAL   Nat Immunol 12:695-708 (2011)

Mientras trato de analizar este formato extraño en algo más sensato (un diccionario que luego se puede convertir a JSON), no estoy seguro de qué hacer: dividir ciegamente los espacios causa un desorden (también afecta la información con espacios), y no estoy seguro de cómo puedo saber cuándo comienza o no una sección. ¿Es la manipulación de texto suficiente para el trabajo o debo usar métodos más sofisticados?

EDIT:

Empecé a usar pyparsing para el trabajo, pero los registros de varias líneas me desconciertan, aquí hay un ejemplo con DRUG:

 from pyparsing import *
 punctuation = ",.'`&-"
 special_chars = "\()[]"

 drug = Keyword("DRUG")
 drug_content = Word(alphanums) + originalTextFor(OneOrMore(Word(
      alphanums + special_chars))) + ZeroOrMore(LineEnd())
 drug_lines = OneOrMore(drug_content)
 drug_parser = drug + drug_lines

Cuando se aplica a las primeras 3 líneas de DROGAS en el ejemplo, obtengo un resultado incorrecto (\n convertido a rendimientos reales para facilitar la lectura):

 ['DRUG', ['D09347', 'Fostamatinib (USAN)
        D09348  Fostamatinib disodium      (USAN)
        D09692  Veliparib (USAN']]

Como puede ver, las entradas posteriores se agrupan todas juntas, mientras que yo esperaría:

 ['DRUG', [['D09347', 'Fostamatinib (USAN)'], ["D09348", "Fostamatinib disodium (USAN)"],
           ['D09692', ' Veliparib (USAN)']]]

preguntado el 04 de julio de 12 a las 09:07

¿Ha intentado dividir en espacios en blanco, pero limitando el número de divisiones? -

@inspectorG4dget: lo pensé, pero las entradas individuales tienen requisitos de espacio variable (por lo que probablemente cada sección requiera su número específico de divisiones) -

¡Ajá! Entonces quizás re.split sería una mejor opción -

También preguntaste esto en el wiki de pyparsing, mira mi respuesta: pyparsing.wikispaces.com/message/view/home/55280466 -

2 Respuestas

Te recomiendo que utilices un enfoque basado en un analizador. Por ejemplo, PLY Python se puede utilizar para la tarea en cuestión.

Respondido 04 Jul 12, 09:07

Probaré esto y las soluciones de Antonio Beamud en breve. Gracias. - Einar

@Einar Otra opción de análisis sería pyparsing.wikispaces.com que tiene documentación razonable y muchos ejemplos - jon clementes

Se agregó una prueba con pyparsing, llegando allí para una sola línea, pero aún teniendo problemas con varias líneas. - Einar

El mejor enfoque es usar expresiones regulares, como:

m = re.compile('^ENTRY\s+(.*)$')
m.search(line)
if m:
   m.groups()[0].strip()

para líneas sin entrada, debe usar la última entrada que detectó.

Un enfoque más simple se divide por entrada, por ejemplo:

vals = line.split('DRUG')
if len(vals) > 1:
     drug_field = vals[1].strip()

Respondido 04 Jul 12, 09:07

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.