La historia de un pequeño proyecto de doce años (sobre BIRMA.NET por primera vez y, francamente, de primera mano)

El nacimiento de este proyecto puede considerarse una pequeña idea que se me ocurrió a finales de 2007 y que estaba destinada a encontrar su forma definitiva sólo 12 años después (en este momento, por supuesto, aunque la implementación actual, según para el autor, es muy satisfactorio).

Todo comenzó cuando, 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 los índices de publicaciones de libros (y música) en la base de datos existente, aparentemente, se puede simplificar y automatizar significativamente, aprovechando la propiedad de orden y repetibilidad de todos los datos necesarios para la entrada, como el nombre del autor del artículo (si hablamos de una colección de artículos), el título de el artículo (o el subtítulo reflejado en el índice) y el número de página del elemento actual del índice. Al principio estaba prácticamente convencido de que en Internet se podía encontrar fácilmente un sistema adecuado para realizar esta tarea. Cuando me sorprendió el hecho de que no podía encontrar un proyecto así, decidí intentar implementarlo por mi cuenta.

Al poco tiempo comenzó a funcionar el primer prototipo, el cual inmediatamente comencé a utilizar en mis actividades diarias, depurándolo simultáneamente en todos los ejemplos que llegaban a mi mano. Afortunadamente, en mi lugar de trabajo habitual, donde de ninguna manera era programador, todavía me salía con la mía con visibles "tiempos de inactividad" en mi trabajo, durante los cuales estaba depurando intensamente mi creación, algo casi impensable en las realidades actuales, que implican Informes diarios sobre el trabajo realizado durante el día. El proceso de perfeccionamiento del programa duró nada menos que un año, pero incluso después el resultado difícilmente podría considerarse completamente exitoso: inicialmente se establecieron demasiados conceptos diferentes que no estaban del todo claros para su implementación: elementos opcionales que pueden ser omitido; visualización anticipada de elementos (con el fin de sustituir elementos anteriores en los resultados de búsqueda); incluso nuestro propio intento de implementar algo así como expresiones regulares (que tienen una sintaxis única). Debo decir que antes de esto había abandonado un poco la programación (durante unos 8 años, si no más), por lo que la nueva oportunidad de aplicar mis habilidades a una tarea interesante y necesaria capturó por completo mi atención. No es sorprendente que el código fuente resultante, en ausencia de enfoques claros para su diseño por mi parte, se convirtiera rápidamente en una mezcla inimaginable de piezas dispares en lenguaje C con algunos elementos de C++ y aspectos de programación visual (inicialmente Se decidió utilizar un sistema de diseño como Borland C++ Builder - “casi Delphi, pero en C”). Sin embargo, todo esto finalmente dio sus frutos al automatizar las actividades diarias de nuestra biblioteca.

Al mismo tiempo, decidí, por si acaso, realizar cursos para formar desarrolladores de software profesionales. No sé si realmente es posible aprender a “ser programador” desde cero allí, pero teniendo en cuenta las habilidades que ya tenía en ese momento, pude dominar de alguna manera tecnologías que eran más relevantes en ese momento, como como C#, Visual Studio para desarrollo bajo .NET, así como algunas tecnologías relacionadas con Java, HTML y SQL. Toda la formación duró un total de dos años y sirvió como punto de partida para otro proyecto mío, que al final se prolongó durante varios años, pero este es un tema para una publicación aparte. Aquí solo sería apropiado señalar que intenté adaptar los desarrollos 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 la use como base para el próximo proyecto de diploma.
Con el tiempo, esta idea empezó a parecerme digna de ser expresada en conferencias anuales con la participación de representantes de varias bibliotecas como “LIBKOM” y “CRIMEA”. La idea, sí, pero no mi implementación en ese momento. Entonces también esperaba que alguien lo reescribiera utilizando enfoques más competentes. De una forma u otra, en 2013 decidí escribir 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 fue aprobada y comencé a realizar 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 capacidades adicionales (no tanto completamente implementadas, sino más bien asumidas): Todos los detalles se pueden encontrar en mi informe..

Para ser honesto, fue difícil llamar a BIRMA 2013 algo completo; Hablando francamente, era una embarcación muy complicada hecha a toda prisa. En términos de código, prácticamente no hubo innovaciones especiales, excepto un intento bastante inútil de crear algún tipo de sintaxis unificada para el analizador, que en apariencia recuerda al lenguaje de formato IRBIS 64 (y de hecho, también al sistema ISIS). con paréntesis como estructuras cíclicas; por qué en ese momento pensé que se veía muy bien). El analizador tropezó irremediablemente con estos círculos de paréntesis del tipo apropiado (ya que los paréntesis también desempeñaban otra función, es decir, marcaban estructuras opcionales durante el análisis que se pueden omitir). Una vez más, remito a todos aquellos que quieran familiarizarse con más detalle con la entonces difícil de imaginar e injustificada sintaxis de BIRMA a mi informe de aquella época.

En general, además de luchar con mi propio analizador, no tengo nada más que decir sobre el código de esta versión, excepto la conversión inversa de las fuentes existentes a C++, preservando al mismo tiempo algunas características típicas del código .NET (para ser honesto, es Es difícil de entender qué me impulsó exactamente a mover todo hacia atrás (probablemente algún miedo estúpido por mantener mis códigos fuente en secreto, como si fuera algo equivalente a la receta secreta de Coca-Cola).

Quizás esta estúpida decisión también sea la razón de las dificultades para emparejar la biblioteca DLL resultante con la interfaz existente de una estación de trabajo casera para ingresar datos en un catálogo electrónico (sí, no mencioné otro hecho importante: de ahora en adelante, todos el código del “motor” BIRMA era el esperado, está separado de la parte de la interfaz y empaquetado en la DLL correspondiente). Por qué fue necesario escribir una estación de trabajo separada para estos fines, que, en su apariencia y método de interacción con el usuario, copió descaradamente la misma estación de trabajo "Catalogizador" del sistema IRBIS 64, esta es una pregunta aparte. En resumen: dio la solidez necesaria a mis desarrollos de entonces para mi proyecto de graduación (de lo contrario, el indigerible motor de análisis por sí solo no era suficiente). Además, encontré algunas dificultades al implementar la interfaz de la estación de trabajo Cataloger con mis propios módulos, implementados tanto en C++ como en C#, y acceder directamente a mi motor.

En general, aunque parezca extraño, fue este prototipo bastante torpe 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 no intenté al menos encontrar formas de 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 podrían incluir elementos opcionales; así es como iba a hacer realidad la idea de plantillas universales para descripciones bibliográficas de publicaciones y varias otras cosas interesantes. Sin embargo, en mis actividades prácticas en ese momento, todo esto tenía poca demanda, y la implementación que tenía en ese momento era suficiente para ingresar tablas de contenido. Además, el vector de desarrollo de nuestra biblioteca empezó a desviarse cada vez más hacia la digitalización de los archivos del museo, la elaboración de informes y otras actividades de poco interés para mí, lo que al final me obligó a abandonarla finalmente, dando paso a quienes estar más contento con todo esto.

Paradójicamente, fue después de estos dramáticos acontecimientos que el proyecto BIRMA, que en ese momento ya tenía todas las características de un típico proyecto de construcción a largo plazo, pareció comenzar a tomar su tan esperada nueva vida. Tuve más tiempo libre para pensamientos ociosos, nuevamente comencé a buscar en la World Wide Web en busca de algo similar (afortunadamente, ahora ya podía adivinar que buscaría todo esto no en cualquier lugar, sino en GitHub), y en algún lugar en el A principios de este año finalmente encontré un producto correspondiente de la conocida empresa Salesforce con el insignificante nombre Gorp. Por sí solo, podría hacer casi todo lo que necesitaba de un motor de análisis de este tipo, es decir, aislar inteligentemente fragmentos individuales de texto arbitrario pero claramente estructurado, al tiempo que tenía una interfaz bastante fácil de usar para el usuario final, que incluía esencias tan comprensibles como un patrón, una plantilla y una ocurrencia, y al mismo tiempo se utiliza la sintaxis familiar de las expresiones regulares, que se vuelve incomparablemente más legible debido a la división en grupos semánticos designados para el análisis.

En general, decidí que este es el indicado. Gorp (Me pregunto qué significa este nombre. ¿Quizás algún tipo de “analizador regular de orientación general”?): exactamente lo que he estado buscando durante mucho tiempo. Es cierto que su implementación inmediata para mis propias necesidades presentaba tal problema que este motor requería un cumplimiento demasiado estricto de la secuencia estructural del texto fuente. Para algunos informes, como los archivos de registro (es decir, los desarrolladores los colocaron como ejemplos claros del uso del proyecto), esto es bastante adecuado, pero para los mismos textos de tablas de contenido escaneadas, es poco probable. Después de todo, la misma página con una tabla de contenido puede comenzar con las palabras "Tabla de contenido", "Contenido" y cualquier otra descripción preliminar que no necesitemos colocar en los resultados del análisis previsto (y cortarlas manualmente). cada vez también es un inconveniente). Además, entre elementos individuales que se repiten, como el nombre del autor, el título y el número de página, la página puede contener cierta cantidad de basura (por ejemplo, dibujos y simplemente caracteres aleatorios), que también sería bueno poder eliminar. cortar. Sin embargo, el último aspecto todavía no era tan significativo, pero debido al primero, la implementación existente no podía comenzar a buscar las estructuras necesarias en el texto desde un lugar determinado, sino que simplemente lo procesaba desde el principio, no encontraba las estructuras necesarias en el texto. patrones específicos allí y... terminé mi trabajo. Obviamente, fueron necesarios algunos ajustes para al menos dejar algo de espacio entre las estructuras repetidas, y eso me permitió volver a trabajar.

Otro problema fue que el proyecto en sí se implementó en Java, y si en el futuro planeaba implementar algún medio para interconectar esta tecnología con aplicaciones familiares para ingresar datos en bases de datos existentes (como el "Cataloguer" de Irbis), entonces al menos Haga esto en C# y .NET. No es que Java en sí sea un mal lenguaje; una vez incluso lo usé para implementar una interesante aplicación de ventana que implementaba la funcionalidad de una calculadora programable doméstica (como parte de un proyecto de curso). Y en términos de sintaxis es muy similar al mismo C sostenido. Bueno, esto es sólo una ventaja: más fácil me resultará finalizar un proyecto existente. Sin embargo, no quería volver a sumergirme en este mundo bastante inusual de las tecnologías Java de ventana (o más bien, de escritorio); después de todo, el lenguaje en sí no estaba "adaptado" para tal uso y no anhelaba en absoluto una repetición de la experiencia anterior. Quizás sea precisamente porque C# junto con WinForms está mucho más cerca de Delphi, con el que muchos de nosotros empezamos alguna vez. Afortunadamente, la solución necesaria se encontró rápidamente: en forma de proyecto IKVM.NET, que facilita la traducción de programas Java existentes a código .NET administrado. Es cierto que los autores ya habían abandonado el proyecto en ese momento, pero su última implementación me permitió llevar a cabo con bastante éxito las acciones necesarias para los textos fuente. Gorp.

Así que hice todos los cambios necesarios y lo ensamblé todo en una DLL del tipo apropiado, que podría ser "recogida" fácilmente por cualquier proyecto para .NET Framework creado en Visual Studio. Mientras tanto, creé otra capa para una presentación conveniente de los resultados obtenidos. Gorp, en forma de estructuras de datos correspondientes que sería conveniente procesar en una vista de tabla (tomando como base tanto filas como columnas; tanto claves de diccionario como í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 para enseñarle a analizar muestras existentes de textos escaneados de tablas de contenido no causó ninguna complicación especial. De hecho, ni siquiera tuve que consultar mis plantillas 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 establecían un marco bastante limitado para los textos que podían analizarse correctamente con su ayuda, el nuevo motor ya hizo posible desarrollar plantillas bastante universales adecuadas para varios tipos de marcado en una vez. Incluso intenté escribir algún tipo de plantilla integral para cualquier texto de índice arbitrario, aunque, por supuesto, incluso con todas las nuevas posibilidades que se abren para mí, incluida, en particular, la capacidad limitada de implementar las mismas secuencias repetidas anidadas ( como, por ejemplo, apellidos e iniciales de varios autores seguidos), esto resultó ser una utopía.

Quizás en el futuro sea posible implementar un cierto concepto de metaplantillas, que podrán verificar que el texto fuente cumpla con varias de las plantillas disponibles a la vez y luego, de acuerdo con los resultados obtenidos, seleccionar la el más adecuado, utilizando algún tipo de algoritmo inteligente. Pero ahora estaba más preocupado por otra cuestión. Un analizador como Gorp, a pesar de toda su versatilidad y las modificaciones que hice, todavía era inherentemente incapaz de hacer algo aparentemente simple que mi analizador escrito por mí mismo podía hacer desde la primera versión. Es decir: tenía la capacidad de encontrar y extraer del texto fuente todos los fragmentos que coincidan con la máscara especificada dentro de la plantilla utilizada en el lugar correcto, sin estar en absoluto interesado en lo que el texto dado 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 la presencia en el texto de conjuntos de caracteres arbitrarios que eran completamente no contabilizado en el análisis, encerrado entre las estructuras repetidas detectadas. Sin embargo, esto no permitió establecer la siguiente máscara independientemente de los resultados de la búsqueda del fragmento anterior utilizando la máscara correspondiente: el rigor de la estructura del texto descrito aún no dejaba lugar para inclusiones arbitrarias de caracteres irregulares.

Y si en los ejemplos de tablas de contenido con los que me encontré este problema aún no parecía tan grave, entonces al intentar aplicar un nuevo mecanismo de análisis a una tarea similar de analizar el contenido de un sitio web (es decir, el mismo análisis), es Las limitaciones están aquí, aparecieron con toda su obviedad. Después de todo, es bastante fácil configurar las máscaras necesarias para los fragmentos de marcado web, entre los cuales deben ubicarse los datos que estamos buscando (que deben extraerse), pero ¿cómo podemos obligar al analizador a pasar inmediatamente al siguiente? ¿Fragmento similar, a pesar de todas las posibles etiquetas y atributos HTML que se pueden colocar en los espacios entre ellos?

Después de pensar un poco, decidí introducir un par de patrones de servicio. (%todos_antes) и (%all_after), cumpliendo el propósito obvio de garantizar que todo lo que pueda estar contenido en el texto fuente se omita antes de cualquier patrón (máscara) que lo siga. Es más, si (%todos_antes) simplemente ignoró todas estas inclusiones arbitrarias, entonces (%all_after), por el contrario, permitió agregarlos al fragmento deseado después de pasar 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, logramos hacer esto (aunque incluso se escribió la primera, aunque con muchos errores, implementación de mi analizador, e incluso más rápido, en un par de semanas). A partir de ahora, el sistema adquirió una forma verdaderamente universal, nada menos que 12 años después de los primeros intentos de ponerlo en funcionamiento.

Por supuesto, este no es el final de nuestros sueños. También puede reescribir completamente el analizador de plantillas gorp en C#, utilizando cualquiera de las bibliotecas disponibles para implementar una gramática gratuita. Creo que el código debería simplificarse significativamente y esto nos permitirá deshacernos del legado en forma de fuentes Java existentes. Pero con el tipo de motor existente, también es muy posible hacer varias cosas interesantes, incluido un intento de implementar las metaplantillas que ya he mencionado, sin mencionar el análisis de varios datos de varios sitios web (sin embargo, no descarto que las herramientas de software especializadas existentes son más adecuadas para esto (simplemente no he tenido la experiencia adecuada en su uso todavía).

Por cierto, este verano ya recibí una invitación por correo electrónico de una empresa que utiliza tecnologías Salesforce (el desarrollador del original Gorp), pasar una entrevista para trabajos posteriores en Riga. Lamentablemente, en este momento no estoy preparado para tales redespliegues.

Si este material despierta algún interés, en la segunda parte intentaré describir con más detalle la tecnología para compilar y posteriormente analizar plantillas usando el ejemplo de la implementación utilizada en Salesforce. Gorp (mis propias adiciones, con la excepción de un par de palabras funcionales ya descritas, prácticamente no realizan cambios en la sintaxis de la plantilla, por lo que casi toda la documentación del sistema original Gorp Adecuado para mi versión también).

Fuente: habr.com

Añadir un comentario