El nacimiento de este proyecto puede considerarse una pequeña idea que me visitó en algún lugar a fines de 2007, que estaba destinado a encontrar su forma final solo 12 años después (en este momento, por supuesto, aunque la implementación actual, en opinión del autor, es muy satisfactoria) .
Todo comenzó con el hecho de que, en el proceso de cumplir con mis deberes oficiales en la biblioteca, llamé la atención sobre el hecho de que el proceso de ingresar datos del texto escaneado de la tabla de contenido de publicaciones de libros (y música) en la base de datos existente, aparentemente, puede simplificarse enormemente. y automatizar, utilizando la propiedad de orden y repetibilidad de todos los datos necesarios para la entrada, como el nombre del autor del artículo (si estamos hablando de una colección de artículos), el nombre del artículo (o el subtítulo reflejado en la tabla de contenido) y La página de la tabla de contenido actual. Al principio, estaba casi convencido de que un sistema adecuado para esta tarea se podía encontrar fácilmente en Internet. Cuando se produjo alguna sorpresa por el hecho de que no pude encontrar un proyecto de este tipo, decidí intentar implementarlo por mi cuenta.
Después de un tiempo bastante corto, el primer prototipo comenzó a funcionar, lo que inmediatamente comencé a usar en mis actividades diarias, al mismo tiempo que lo depuraba con todos los ejemplos que se me ocurrían. Afortunadamente, en mi lugar de trabajo habitual, donde de ninguna manera era un programador, todavía podía salir con “tiempos de inactividad” visibles en el trabajo, durante los cuales trabajé duro para depurar mi creación, algo casi impensable en las realidades de hoy, lo que implica informes diarios sobre El trabajo realizado durante el día. El proceso de pulido del programa tomó un total de no menos de un año, pero incluso después de eso, el resultado difícilmente podría considerarse completamente exitoso: había demasiados conceptos diferentes que no eran del todo inteligibles para implementar desde el principio: elementos opcionales que se podían omitir; vista principal de elementos (con el propósito de sustituir los resultados de búsqueda de elementos anteriores); incluso su propio intento de implementar algo como expresiones regulares (que tienen una sintaxis distintiva). Debo decir que antes de eso logré lanzar un poco la programación (durante aproximadamente 8 años, si no más), por lo que una nueva oportunidad para aplicar mis habilidades a una tarea interesante y necesaria captó completamente mi atención. No es sorprendente que el código fuente resultante, en ausencia de enfoques inteligibles para diseñarlo para mí, se convirtiera rápidamente en una mezcla inimaginable de piezas dispares en el lenguaje C con algunos elementos C ++ y aspectos de la programación visual (originalmente se decidió usar un sistema de diseño como Borland C ++ Builder - "casi Delphi, pero en C"). Sin embargo, todo esto finalmente valió la pena en la automatización de las actividades diarias de nuestra biblioteca.
Al mismo tiempo, decidí, por si acaso, tomar cursos de capacitación para desarrolladores de software profesionales. No sé si es posible aprender realmente "de un programador" desde cero, pero teniendo en cuenta las habilidades que ya tenía en ese momento, pude dominar tecnologías un poco más avanzadas como C #, Visual Studio para desarrollar bajo. NET, así como algunas de las tecnologías relacionadas con Java, HTML y SQL. Toda la capacitación tomó un total de dos años y sirvió como punto de partida para otro de mis proyectos, que finalmente se extendió durante varios años, pero este ya es un tema para una publicación por separado. Aquí solo será pertinente señalar que intenté adaptar la experiencia que ya tenía en el proyecto descrito para crear una aplicación de ventana completa en C # y WinForms que implemente la funcionalidad necesaria, y ponerla en la base del próximo proyecto de graduación.
Con el tiempo, esta idea comenzó a parecer digna de ser expresada en tales conferencias anuales con la participación de representantes de varias bibliotecas, como LIBCOM y CRIMEA. La idea es sí, pero de ninguna manera me doy cuenta de ese momento. Entonces también esperaba, entre otras cosas, que alguien lo reescribiera utilizando enfoques más competentes. De una forma u otra, para 2013, decidí elaborar un informe sobre mi trabajo preliminar y enviarlo al Comité Organizador de la Conferencia con una solicitud de subvención para participar en la conferencia. Para mi sorpresa, mi solicitud estaba satisfecha y comencé a hacer algunas mejoras en el proyecto para prepararlo para su presentación en la conferencia.
En ese momento, el proyecto ya había recibido un nuevo nombre, BIRMA, adquirió varias oportunidades adicionales (no tan completas como se esperaba):
todos los detalles se pueden encontrar en mi informe .
Francamente, el 2013 BIRMA fue difícil de llamar algo completo; francamente, era un látigo hecho artesanía muy hacky. En cuanto a la parte del código, prácticamente no hubo innovaciones especiales, aparte del intento bastante impotente de crear algún tipo de sintaxis unificada para el analizador, que en apariencia se parece al lenguaje de formato IRBIS 64 (y, de hecho, incluso ISIS, con paréntesis en el papel de las estructuras cíclicas; por qué entonces me pareció que se ve muy bien). El analizador tropezó irremediablemente con estos remolinos desde los corchetes del tipo correspondiente (ya que los paréntesis desempeñaron el mismo papel allí, es decir, marcaron estructuras opcionales que podrían omitirse durante el análisis). Todos los que quieran familiarizarse con más detalle con la sintaxis BIRMA injustificada, entonces difícil de imaginar, vuelvo a consultar mi informe de esa época.
En general, excepto por la lucha con nuestro propio analizador, entonces, en lo que respecta al código de esta versión, no tengo nada más que decir, excepto la conversión inversa de las fuentes disponibles en C ++ con la preservación de algunas características típicas del código .NET (para ser honesto, es difícil de entender exactamente lo que me llevó a transferir todo de regreso, probablemente una especie de miedo loco por mantener en secreto mis códigos fuente, como si fuera algo equivalente a la receta secreta de Coca-Cola).
Tal vez esta estúpida decisión también contenga la razón de las dificultades para emparejar la DLL resultante con la interfaz de estación de trabajo propia hecha para ingresar datos en el catálogo electrónico (sí, no mencioné otro hecho importante: de ahora en adelante, todo el código del motor BIRMA era como se esperaba, separado de la interfaz y empaquetado en la DLL apropiada). ¿Por qué necesitaba escribir una estación de trabajo separada para estos propósitos, que de todos modos, en su apariencia y en la forma de interactuar con el usuario, copió descaradamente la misma estación de trabajo "Catalogizer" del sistema IRBIS 64? Este es un tema aparte. En resumen: respetó mis logros de la época para el proyecto de graduación (de lo contrario, el motor analizador indigestible por sí solo no era suficiente). Además, encontré algunas dificultades al implementar el emparejamiento de la estación de trabajo "Catalogizer" con mis propios módulos implementados tanto en C ++ como en C #, y dirigirlos directamente a mi motor.
En general, por extraño que parezca, pero fue este prototipo bastante incómodo del futuro BIRMA.NET el que estaba destinado a convertirse en mi "caballo de batalla" durante los próximos cuatro años. No se puede decir que durante este tiempo ni siquiera intenté encontrar formas para una implementación nueva y más completa de una idea de larga data. Entre otras innovaciones, ya debería haber secuencias cíclicas anidadas, que también podrían incluir elementos opcionales, así es como me iba a dar cuenta de la idea de plantillas universales para la descripción bibliográfica de publicaciones y otras cosas interesantes. Sin embargo, en mi práctica en ese momento, todo esto era poco exigido, y la implementación que tuve en ese momento fue suficiente para introducir la tabla de contenido. Además, el vector de la dirección de desarrollo de nuestra biblioteca comenzó a desviarse cada vez más hacia la digitalización de los archivos del museo, la generación de informes y otras actividades que fueron de poco interés para mí, lo que al final me hizo dejarlo por completo, dando paso a aquellos a quienes hubiera sido más agradable .
Paradójicamente, pero precisamente después de estos dramáticos eventos, el proyecto BIRMA, que en ese momento ya poseía todos los rasgos característicos de una construcción típica a largo plazo, ¡parecía comenzar a ganar su nueva vida tan esperada! Tenía más tiempo libre para pensamientos ociosos, nuevamente comencé a buscar en la World Wide Web en busca de algo similar (bueno, ahora ya podía adivinar buscar todo esto desde cualquier lugar, es decir, en GitHub), y en algún lugar de A principios de este año, finalmente me encontré con el oficio correspondiente de la conocida oficina de Salesforce con el nombre sin importancia
Gorp . Por sí solo, podría hacer casi todo lo que necesitaba de un motor de análisis de este tipo, a saber, aislar de forma inteligente fragmentos individuales de un sistema arbitrario, pero con una estructura clara del texto, al tiempo que tiene una interfaz bastante digerible para el usuario final, incluida esa claridad entidades como un patrón, patrón y ocurrencia, y al mismo tiempo que involucra la sintaxis habitual de las expresiones regulares, que se vuelve incomparablemente más legible en virtud de dividirse en grupos semánticos significativos para el análisis.
En general, decidí que este mismo
Gorp (¿me pregunto qué significa este nombre? ¿Quizás algún “analizador regular orientado a lo general”?) Es exactamente lo que he estado buscando durante mucho tiempo. Es cierto que su implementación inmediata para mis propias necesidades tuvo un problema tal que este motor requirió una adherencia demasiado estricta a la secuencia estructural del texto fuente. Para algunos informes, como los archivos de registro (es decir, los desarrolladores los colocaron como ejemplos visuales del uso del proyecto), esto funcionará bien, pero para los mismos textos la tabla de contenido escaneada es poco probable. Después de todo, la misma página con la tabla de contenido puede comenzar con las palabras "Tabla de contenido", "Contenido" y algunas otras descripciones preliminares que no necesitamos para incluir en los resultados del análisis propuesto (y cortarlas manualmente cada vez también es un inconveniente). Además, entre elementos repetitivos individuales, como el nombre del autor, el título y el número de página, la página puede contener una cierta cantidad de basura (por ejemplo, imágenes y solo caracteres aleatorios), que también sería bueno poder cortar. Sin embargo, el último aspecto aún no era tan significativo, pero en virtud del primero, la implementación existente no pudo comenzar a buscar las estructuras necesarias en el texto desde algún lugar específico, sino que simplemente lo procesó desde el principio, no encontró los patrones especificados allí y ... terminó tu trabajo Obviamente, se requería una revisión apropiada, que permitiría al menos dejar algunas brechas entre las estructuras repetitivas, y esto me hizo sentarme nuevamente en el trabajo.
Otro problema fue que el proyecto en sí mismo se implementó en Java, y si planeaba implementar aún más algunos medios de interconectar esta tecnología con las aplicaciones habituales para ingresar datos en bases de datos existentes (como el catalogador Irbis), entonces al menos menos hacerlo en C # y .NET. No es que Java en sí fuera un mal lenguaje, una vez que incluso implementé en él una aplicación de ventana poco interesante que implementa la funcionalidad de una calculadora programable doméstica (como parte de un proyecto de curso). Sí, y en sintaxis es muy similar al mismo C-sharpe. Bueno, esto es solo una ventaja: cuanto más fácil sea para mí finalizar un proyecto existente. Sin embargo, no quería sumergirme en este mundo bastante inusual de tecnologías Java de ventana (o más bien de escritorio); al final, el lenguaje en sí no se "agudizó" para tal uso, y no esperé la repetición de la experiencia previa. Tal vez sea porque C # junto con WinForms está mucho más cerca de Delphi, que muchos de nosotros comenzamos una vez. Afortunadamente, la solución correcta se encontró con bastante rapidez, en la persona del proyecto
IKVM.NET , lo que facilita la traducción de los programas Java existentes en código administrado .NET. Es cierto que el proyecto en sí ya fue abandonado por los autores en ese momento, pero su última implementación me permitió hacer con bastante éxito las acciones necesarias para los textos fuente de
Gorp .
Así que hice todos los cambios necesarios y lo puse todo en una DLL del tipo apropiado, que cualquier proyecto para .NET Framework creado en Visual Studio podría fácilmente "recoger".
Mientras tanto , creé otra capa para una representación conveniente de los resultados devueltos por
Gorp , en forma de estructuras de datos correspondientes que serían convenientes para procesar en una representación de tabla (y tomando como base tanto filas como columnas; claves de diccionario e índices numéricos) . Bueno, las utilidades necesarias para procesar y mostrar los resultados se escribieron con bastante rapidez.
Además, el proceso de adaptación de plantillas para el nuevo motor no causó ninguna complicación especial para enseñarle cómo desmontar muestras existentes de textos de tabla de contenido escaneados. De hecho, ni siquiera tuve que recurrir a mis espacios en blanco anteriores: simplemente creé todas las plantillas necesarias desde cero. Además, si las plantillas diseñadas para funcionar con la versión anterior del sistema establecen un marco bastante estrecho para textos que podrían analizarse correctamente con su ayuda, el nuevo motor ya permitía el desarrollo de plantillas bastante universales adecuadas para varios tipos de marcado a la vez. Incluso intenté escribir una plantilla completa para cualquier texto de tabla de contenido arbitrario, aunque, por supuesto, incluso con todas las nuevas posibilidades que se abren para mí, incluida, en particular, la capacidad limitada para implementar las mismas secuencias repetidas anidadas (como, por ejemplo, apellidos e iniciales varios autores seguidos), esto resultó ser una utopía.
Es posible que en el futuro sea posible implementar un cierto concepto de meta-plantillas que puedan verificar el cumplimiento del texto fuente con varias de las plantillas disponibles a la vez, y luego, de acuerdo con los resultados obtenidos, elegir la más adecuada utilizando algún algoritmo inteligente. Pero ahora estaba más preocupado por otra pregunta. Un analizador como
Gorp , a pesar de toda su versatilidad y modificaciones hechas por mí, aún era incapaz por naturaleza de realizar una cosa aparentemente simple que mi propio analizador escrito a mano fue capaz de hacer desde la primera versión. A saber: tenía la capacidad de encontrar y extraer del texto fuente todos los fragmentos que coinciden con la máscara especificada en el marco de la plantilla utilizada en el lugar correcto, aunque no le interesa en absoluto lo que el texto contiene en los espacios entre estos fragmentos. Hasta ahora, solo he mejorado ligeramente el nuevo motor, permitiéndole buscar todas las posibles nuevas repeticiones de una secuencia dada de tales máscaras desde la posición actual, dejando la posibilidad de que el texto no se tenga en cuenta por completo al analizar conjuntos de caracteres arbitrarios encerrados entre estructuras repetitivas detectadas. Sin embargo, esto no permitió establecer la siguiente máscara independientemente de los resultados de búsqueda del fragmento anterior por la máscara correspondiente: la rigurosidad de la estructura descrita del texto todavía no dejaba espacio para inclusiones arbitrarias de caracteres irregulares.
Y si para los ejemplos de la tabla de contenido que se me ocurrió, este problema no parecía tan grave todavía, entonces, al intentar aplicar el nuevo mecanismo de análisis a una tarea similar en esencia para analizar el contenido del sitio web (es decir, el mismo análisis), sus limitaciones están aquí aparecieron con toda su evidencia. Después de todo, es bastante simple establecer las máscaras necesarias para los fragmentos de marcado web, entre los cuales deben estar los datos que estamos buscando (que debe extraer), pero cómo hacer que el analizador vaya inmediatamente al siguiente fragmento similar, a pesar de todas las posibles etiquetas y atributos HTML que pueden caber en las brechas entre ellos?
Después de pensar un poco, decidí introducir un par de patrones de utilidad
(% all_before) y
(% all_after) , que tienen el propósito obvio de garantizar omisiones de todo lo que puede estar contenido en el texto fuente antes de cualquier patrón posterior (máscara). Además, si
(% all_before) simplemente ignoró todas estas inclusiones arbitrarias, entonces
(% all_after) , por el contrario, permitió que se agregaran al fragmento deseado después de cambiar del fragmento anterior. Suena bastante simple, pero para implementar este concepto, tuve que "revisar" las fuentes de gorp nuevamente para hacer las modificaciones necesarias para no romper la lógica ya implementada. Al final, logré hacerlo (aunque incluso la muy, muy primera, aunque la implementación con errores de mi analizador se escribió y aún más rápido, en un par de semanas).
A partir de ahora, el sistema ha adquirido un aspecto verdaderamente universal, no más de 12 años después de los primeros intentos para que funcione.Por supuesto, este no es el último sueño. Todavía puede reescribir completamente el analizador de plantillas de gorp en C # utilizando cualquiera de las bibliotecas disponibles para implementar gramática gratuita. Creo que el código debería simplificarse enormemente, y esto eliminará el legado en forma de fuentes existentes en Java. Pero con el tipo de motor existente, también es bastante posible hacer varias cosas interesantes, incluido un intento de implementar las meta-plantillas que ya he mencionado, sin mencionar el análisis de varios datos de varios sitios web (sin embargo, no excluyo que las herramientas de software especializadas existentes sean más adecuadas para esto - Simplemente no tenía la experiencia relevante de usarlos).Por cierto, este verano ya recibí una invitación por correo electrónico de una empresa que utiliza la tecnología Salesforce (el desarrollador del Gorp original ) para pasar una entrevista para seguir trabajando en Riga. Desafortunadamente, por el momento no estoy listo para tales reubicaciones.La segunda parte describe con más detalle la tecnología para compilar y posteriormente analizar plantillas utilizando el ejemplo de implementación utilizado en Salesforce Gorp (mis propias adiciones, con la excepción de un par de palabras de servicio ya descritas, prácticamente no cambian la sintaxis de la plantilla en sí, por lo que casi toda la documentación del sistema original GorpApto para mi versión). Allí encontrará un código de muestra para interactuar con este motor y un enlace al repositorio de mi versión: Gorp.NET .