Gorp.NET: una nueva biblioteca para crear plantillas inversas para extraer datos de texto estructurado

Gorp.NET es una nueva biblioteca para crear plantillas reversibles para extraer datos de texto estructurado, basado en la base de código existente de Salesforce Gorp .

En esta publicación, hablaré un poco sobre cómo usar la biblioteca para analizar texto estructurado llamado Gorp (uno de los ejemplos de herramientas que a veces se denominan sistemas de plantillas de ingeniería inversa ).
¿Qué es una plantilla reversible en general? Supongamos que tenemos un cierto sistema que nos permite generar el texto que necesitamos en función de los datos iniciales que hemos determinado, de acuerdo con reglas estrictas definidas por la sintaxis de las plantillas. Ahora imaginemos una tarea que tiene un significado opuesto: tenemos un texto que tiene cierta integridad estructural que podría lograrse mediante el uso de un sistema basado en las plantillas del ejemplo anterior. Nuestro objetivo es extraer de este texto los datos de origen sobre la base de los cuales se formaron. Si tratamos de llegar a una cierta sintaxis generalizada para resolver este problema, suministrada al analizador correspondiente, que analiza el texto de entrada en elementos separados, este será un ejemplo de sintaxis para implementar el concepto de plantillas reversibles.

¿Por qué decidí escribir específicamente sobre Gorp ? El hecho es que decidí tomar este sistema como base para finalizar mi propio proyecto: la historia del proyecto en sí, incluidos algunos detalles de todos los cambios que hice al proyecto original de Gorp , se pueden encontrar en el artículo anterior . Aquí nos centraremos con precisión en la parte técnica, incluso en relación con el uso de una versión modificada del motor. Por conveniencia, continuaré llamándolo Gorp.NET , aunque en realidad no se trata de una versión de Gorp que no está portada a .NET, sino solo una versión ligeramente pulida y finalizada, todo en el mismo Java. Otra cosa es que el complemento sobre la propia biblioteca Gorp (en mi versión) en forma de una biblioteca DLL administrada llamada BIRMA.NET usa su propio ensamblado especial: el mismísimo Gorp.NET , que usted mismo puede obtener fácilmente si ejecuta el texto fuente ( la dirección de su repositorio es https://github.com/S-presso/gorp/ ) a través de la utilidad IKVM.NET .

Notaré en este momento que para todo tipo de tareas de extracción de datos de cualquier texto estructurado, las herramientas Gorp.NET en sí mismas serán suficientes para usted , al menos si tiene un pequeño comando de Java o al menos sabe cómo llamar a métodos de módulos Java externos en sus proyectos en .NET Framework, además de incluir varios tipos de las bibliotecas JVM estándar allí (logré esto a través del mismo IKVM.NET , que, sin embargo, ahora ya tiene el estado de un proyecto no compatible). Bueno, y qué harás después con los datos extraídos; esto, como dicen, es asunto tuyo. Gorp y Gorp.NET solo proporcionan un marco básico . Algunas bases para el procesamiento posterior de todos esos datos contienen el mencionado BIRMA.NET . Pero la descripción de la funcionalidad BIRMA.NET en sí misma ya es un tema para una publicación separada (aunque ya logré mencionar algo en mi revisión histórica comparativa previa de las tecnologías BIRMA ). Aquí, mirando hacia el futuro, me permitiré una declaración un tanto audaz de que la tecnología utilizada para describir las plantillas reversibles utilizadas en Gorp.NET (y, en consecuencia, BIRMA.NET ) es algo única entre otras manualidades de este tipo (digo " manualidades ”, ya que las grandes empresas de alguna manera todavía no han sido vistas por mí para promover sus propios marcos para estos fines, bueno, tal vez, solo Salesforce en sí con su implementación original de Gorp ).

Para la divulgación más completa del concepto y los aspectos técnicos que subyacen al sistema de descripción de plantilla utilizado en Gorp, solo dejo aquí un enlace a la documentación original en inglés . Todo lo que se indica en él, puede aplicarlo con seguridad en relación con Gorp.NET . Y ahora te contaré un poco sobre la esencia.

Por lo tanto, la descripción de la plantilla es un tipo de documento de texto (tal vez incluso presentado como una sola línea grande, que se puede pasar al método correspondiente para su procesamiento). Consta de tres partes que contienen descripciones secuenciales de las tres entidades más importantes: patrones , patrones y muestras (extractos).

El bloque de nivel más bajo aquí son patrones : solo pueden consistir en expresiones regulares y referencias a otros patrones. El siguiente nivel de la jerarquía está ocupado por plantillas , cuya descripción también contiene enlaces a patrones, que también pueden nombrarse, así como inclusiones en forma de literales de texto, enlaces a plantillas anidadas y extractores. También hay patrones paramétricos que no tocaré en este momento (en la documentación fuente hay algunos ejemplos de su uso). Bueno, y finalmente, hay ejemplos que especifican reglas sintácticas específicas que asocian patrones nombrados con ocurrencias específicas del texto fuente.

Según tengo entendido, el objetivo original que los creadores de Gorp se propusieron era analizar las secuencias de datos contenidas en los archivos de informe (o archivos de registro). Considere un ejemplo simple de una aplicación específica del sistema.

Supongamos que tenemos un informe que contiene la siguiente línea:

<86> 2015-05-12T20: 57: 53.302858 + 00: 00 10.1.11.141 RealSource: "10.10.5.3"


Compongamos una plantilla de ejemplo para analizarla usando las herramientas de Gorp :

pattern %phrase \\S+
pattern %num \\d+\n
pattern %ts %phrase
pattern %ip %phrase

extract interm {
template <%num>$eventTimeStamp(%ts) $logAgent(%ip) RealSource: "$logSrcIp(%ip)"
}


Tenga en cuenta que el bloque de asignación de plantillas incluso se omite aquí, ya que todas las plantillas necesarias ya están incluidas en la selección final. Todas las plantillas utilizadas aquí se nombran, sus contenidos se indican entre paréntesis después de su nombre. Como resultado, se creará un conjunto de datos de texto con los nombres eventTimeStamp , logAgent y logSrcIp .

Ahora escribiremos un programa simple para extraer los datos necesarios. Supongamos que la plantilla que creamos ya está contenida en un archivo llamado extractions.xtr .
 import com.salesforce.gorp.DefinitionReader; import com.salesforce.gorp.ExtractionResult; import com.salesforce.gorp.Gorp; // ... DefinitionReader r = DefinitionReader.reader(new File("extractions.xtr")); Gorp gorp = r.read(); final String TEST_INPUT = "<86>2015-05-12T20:57:53.302858+00:00 10.1.11.141 RealSource: \"10.10.5.3\""; ExtractionResult result = gorp.extract(TEST_INPUT); if (result == null) { // no match, handle throw new IllegalArgumentException("no match!"); } Map<String,Object> properties = asMap(); // and then use extracted property values 


Otro ejemplo de una plantilla de análisis simple:

# Patterns
pattern %num \d+
pattern %hostname [a-zA-Z0-9_\-\.]+
pattern %status \w+

# Templates
@endpoint $srcHost(%hostname): $srcPort(%num)

# Extraction
extract HostDefinition {
template @endpoint $status(%status)
}


Bueno, creo que el punto está claro. Tampoco estará mal mencionar que para el método de extracción también hay una definición con dos parámetros de entrada, el segundo de los cuales tiene un tipo lógico. Si lo establece en verdadero , cuando se ejecute, el método iterará sobre todos los conjuntos de datos potenciales, hasta que encuentre uno adecuado (también puede reemplazar la llamada al método con extractSafe , ya sin el segundo parámetro). El valor predeterminado es falso , y el método puede "jurar" en la discrepancia entre los datos de entrada y la plantilla utilizada.
Observo al mismo tiempo que Gorp.NET también introdujo una nueva implementación extendida del método de extracción : ahora hay una versión con dos parámetros posteriores de tipo lógico. Usando una llamada abreviada a la vista extractAllFound , establecemos ambos en true de forma predeterminada. El valor positivo del tercer parámetro nos da un alcance aún mayor para las variaciones: a partir de ahora, podemos analizar el texto con cualquier inclusión de caracteres arbitrarios en los intervalos entre las muestras deseadas, ya estructuradas (que contienen conjuntos de datos extraídos).

Entonces, ha llegado el momento de responder la pregunta: ¿qué puede ser exactamente único en esta modificación de la versión básica de Gorp , además de la extensión del método de extracción?
El hecho es que cuando hace varios años ya creé una especie de mi propia herramienta para extraer los datos requeridos del texto (que también se basó en el procesamiento de ciertas plantillas con su propia sintaxis específica), funcionó con principios ligeramente diferentes. Su principal diferencia con el enfoque implementado en Gorp y en todos los marcos derivados es que cada elemento de texto a extraer se estableció simplemente enumerando sus bordes izquierdo y derecho (cada uno de los cuales podría ser parte del elemento en sí o simplemente separarlo de todo el texto posterior o anterior). Al mismo tiempo, de hecho, en el caso general, la estructura del texto fuente en sí no se analizó, como es el caso en Gorp , pero solo se seleccionaron las piezas necesarias. En cuanto al contenido del texto que está encerrado entre ellos, no podría haber sucumbido a ningún análisis estructural (podría ser un conjunto de caracteres incoherente).

¿Es posible lograr un efecto similar en Gorp ? En su versión inicial, quizás no (corríjame si me equivoco al respecto). Si simplemente escribimos una expresión como (. *) , Seguida inmediatamente por la máscara para especificar el borde izquierdo del siguiente elemento a buscar, luego, utilizando el cuantificador de "codicia", se capturará todo el texto posterior. Y no podemos usar clientes habituales con sintaxis "no codiciosa" en las implementaciones existentes de Gorp .
Gorp.NET le permite sortear este problema sin problemas introduciendo dos tipos especiales de patrones: (% all_before) y (% all_after) . El primero de ellos, de hecho, es una alternativa a la versión "no codiciosa" (. *) , Adecuada para compilar sus propias plantillas. En cuanto a (% all_after) , también mira el texto fuente hasta la primera aparición de la siguiente parte del patrón descrito, pero ya se basa en el resultado de búsqueda del patrón anterior. Todo lo que se encuentre entre ellos también caerá en la subcadena extraíble del elemento actual. En cierto sentido (% all_after) "mira hacia atrás" y (% all_before) , por el contrario, "mira hacia adelante". Observo que un análogo único para (% all_before) en la primera versión de BIRMA era el borde izquierdo que faltaba en la descripción del elemento, y análogo a (% all_after) , respectivamente, era un vacío en lugar del borde derecho. Si no se establecen ambos límites al describir el siguiente elemento, entonces el analizador obviamente captura todo el texto posterior. Sin embargo, toda esta implementación de BIRMA ahora tiene un significado puramente histórico (puede leer un poco más al respecto en mi informe de esa época ).
Texto oculto
Los códigos fuente nunca se han presentado en ningún lugar debido a su calidad extremadamente baja; en verdad, podrían servir como un monumento al diseño deficiente de los sistemas de software.


Veamos las características del uso de patrones de servicio (% all_before) y (% all_after) usando el ejemplo de la tarea de extraer datos específicos del usuario de un sitio web específico. Analizaremos el sitio de Amazon, y específicamente esta página: https://www.amazon.com/B06-Plus-Bluetooth-Receiver-Streaming/product-reviews/B078J3GTRK/ ).
Texto oculto
Se toma un ejemplo de una tarea de prueba para la vacante de un desarrollador con especialización en análisis de datos, enviada por mi empresa, que, lamentablemente, no ha respondido a mi solución propuesta al problema. Es cierto, solo me pidieron que describiera el proceso general de la solución, sin proporcionar un algoritmo específico, y en respuesta ya intenté referirme a las plantillas de Gorp, mientras que mis propias extensiones en ese momento solo existían, como dicen, "en papel" ".
En aras de la curiosidad, me permitiré citar un fragmento de mi carta de respuesta, que, aparentemente, es la primera mención de Gorp.NET , aunque de carácter privado.
"Para hacer que la lista anterior de expresiones regulares que utilicé para resolver este problema sea más visual, compilé una plantilla lista para usar (adjuntada a la carta), que puede usarse para extraer todos los datos necesarios aplicando mi propio desarrollo de una naturaleza más universal, solo diseñado para resolver este tipo de problema. Su código se basa en el proyecto github.com/salesforce/gorp , y en la misma página hay una descripción general de las reglas para compilar dichas plantillas. En términos generales, tal descripción en sí misma implica la asignación de expresiones regulares concretas y la lógica de su procesamiento. El punto más difícil aquí es que para cada muestra de datos debemos describir completamente a través de los regulares la estructura completa del texto que los contiene, y no solo los elementos individuales en sí (como podría hacerse al escribir nuestro propio programa que busca secuencialmente en un ciclo, como I descrito anteriormente) ".

La tarea original era recopilar los siguientes datos de la página anterior:

  • Nombre de usuario
  • Calificación
  • Título de revisión
  • La fecha
  • Texto


Bueno, ahora solo le daré una plantilla compilada por mí, que le permite completar esta tarea de manera rápida y eficiente. Creo que el significado general debería ser bastante obvio: quizás usted mismo pueda ofrecer una solución más concisa.
Texto oculto
Fue en este ejemplo que, en general, depuré la funcionalidad de mis propias extensiones para Gorp (ya sin ningún objetivo de empleo, sino más bien basado en la ideología de "Prueba de concepto").


pattern %optspace ( *)
pattern %space ( +)

pattern %cap_letter [AZ]
pattern %small_letter [az]
pattern %letter (%cap_letter|%small_letter)
pattern %endofsentence (\.|\?|\!)+
pattern %delim (\.|\?|\!\,|\:|\;)
pattern %delim2 (\(|\)|\'|\")

pattern %word (%letter|\d)+
pattern %ext_word (%delim2)*%word(%delim)*(%delim2)*

pattern %text_phrase %optspace%ext_word(%space%ext_word)+
pattern %skipped_tags <([^>]+)>

pattern %sentence (%text_phrase|%skipped_tags)+(%endofsentence)?

pattern %start <div class=\"a-fixed-right-grid view-point\">

pattern %username_start <div class=\"a-profile-content\"><span class=\"a-profile-name\">
pattern %username [^\s]+
pattern %username_end </span>

pattern %user_mark_start <i data-hook=\"review-star-rating\"([^>]+)><span class=\"a-icon-alt\">
pattern %user_mark [^\s]+
pattern %user_mark_end ([^<]+)</span>

pattern %title_start data-hook=\"review-title\"([^>]+)>(%skipped_tags)*
pattern %title [^<]+
pattern %title_end </span>

pattern %span class <span class=\"[^\"]*\">

pattern %date_start <span data-hook="review-date"([^>]+)>
pattern %date ([^<]+)
pattern %date_end </span>

pattern %content_start <span data-hook=\"review-body\"([^>]+)>(%skipped_tags)*
pattern %content0 (%sentence)+
pattern %content (%all_after)
pattern %content_end </span>

template @extractUsernameStart (%all_before)%username_start
template @extractUsername $username(%username)%username_end
template @extractUserMarkStart (%all_before)%user_mark_start
template @extractUserMark $user_mark(%user_mark)%user_mark_end
template @extractTitleStart (%all_before)%title_start
template @extractTitle $title(%title)%title_end
template @extractDateStart (%all_before)%date_start
template @extractDate $date(%date)%date_end
template @extractContentStart (%all_before)%content_start
template @extractContent $content(%content)%content_end

extract ToCEntry {
template @extractUsernameStart@extractUsername@extractUserMarkStart@extractUserMark@extractTitleStart@extractTitle@extractDateStart@extractDate@extractContentStart@extractContent
}



Eso es probablemente todo por hoy. Sobre las herramientas de terceros que he implementado, en las que este marco ya ha estado completamente involucrado, puedo decirte en otro momento.

Source: https://habr.com/ru/post/476778/


All Articles