A historia dun pequeno proxecto de doce anos de duración (sobre BIRMA.NET por primeira vez e francamente de primeira man)

O nacemento deste proxecto pódese considerar unha pequena idea que se me ocorreu nalgún lugar a finais de 2007, que estaba destinada a atopar a súa forma definitiva só 12 anos despois (neste momento, por suposto, aínda que a actual implementación, segundo para o autor, é moi satisfactorio).

Todo comezou cando, no proceso de cumprir as miñas entón funcións oficiais na biblioteca, chamei a atención sobre o feito de que o proceso de introducir datos do texto escaneado das táboas de contidos de publicacións de libros (e música) na base de datos existente, ao parecer, pódese simplificar e automatizar significativamente, aproveitando a propiedade da orde e da repetibilidade de todos os datos necesarios para a entrada, como o nome do autor do artigo (se estamos a falar dunha colección de artigos), o título de o artigo (ou o subtítulo reflectido no índice) e o número de páxina do elemento actual do índice. Nun principio, estaba practicamente convencido de que un sistema axeitado para levar a cabo esta tarefa se podía atopar facilmente en Internet. Cando algunha sorpresa foi causada polo feito de non atopar un proxecto deste tipo, decidín tentar implementalo pola miña conta.

Despois de pouco tempo, comezou a funcionar o primeiro prototipo, que de inmediato empecei a utilizar nas miñas actividades diarias, depurándoo simultáneamente en todos os exemplos que me chegaron á man. Afortunadamente, no meu lugar de traballo habitual, onde non era de ningún xeito programador, aínda saín con visibles "tempos de inactividade" no meu traballo, durante o cal estaba a depurar intensamente a miña idea, cousa case impensable nas realidades actuais, que implican informes diarios sobre o traballo realizado durante o día. O proceso de pulido do programa levou un total de nada menos que un ano, pero aínda despois diso dificilmente o resultado podería considerarse completamente exitoso: inicialmente establecéronse demasiados conceptos diferentes que non estaban totalmente claros para a súa implementación: elementos opcionais que poden ser saltado; visualización avanzada de elementos (co propósito de substituír elementos anteriores nos resultados da busca); incluso o noso propio intento de implementar algo así como expresións regulares (que ten unha sintaxe única). Debo dicir que antes disto deixara algo de programar (durante uns 8 anos, se non máis), polo que a nova oportunidade de aplicar as miñas habilidades a unha tarefa interesante e necesaria chamou por completo a miña atención. Non é de estrañar que o código fonte resultante -a falta de enfoques claros para o seu deseño pola miña parte- se convertese moi rapidamente nunha mestura inimaxinable de pezas dispares na linguaxe C con algúns elementos de C++ e aspectos da programación visual (inicialmente decidiuse utilizar un sistema de deseño como o Borland C++ Builder - "case Delphi, pero en C"). Porén, todo isto deu os seus froitos ao final na automatización das actividades cotiás da nosa biblioteca.

Ao mesmo tempo, decidín, por se acaso, facer cursos para formar desenvolvedores de software profesionais. Non sei se é realmente posible aprender a “ser programador” desde cero, pero tendo en conta as habilidades que xa tiña naquel momento, puiden dominar algo tecnoloxías que eran máis relevantes nese momento, como como C#, Visual Studio para o desenvolvemento baixo .NET, así como algunhas tecnoloxías relacionadas con Java, HTML e SQL. Toda a formación levou un total de dous anos e serviu como punto de partida para outro proxecto meu, que finalmente se estendeu durante varios anos, pero este é un tema para unha publicación separada. Aquí só sería apropiado sinalar que tentei adaptar os desenvolvementos que xa tiña no proxecto descrito para crear unha aplicación de fiestra completa en C# e WinForms que implemente a funcionalidade necesaria e utilizala como base para o próximo proxecto de diploma.
Co paso do tempo, esta idea comezou a parecerme digna de ser expresada en conferencias anuais, coa participación de representantes de varias bibliotecas como "LIBKOM" e "CRIMEA". A idea, si, pero non a miña posta en práctica daquela. Entón tamén esperaba que alguén o reescribise usando enfoques máis competentes. Dun xeito ou doutro, no ano 2013 decidín escribir un informe sobre o meu traballo preliminar e envialo ao Comité Organizador do Congreso cunha solicitude de subvención para participar no congreso. Para a miña un tanto sorpresa, a miña solicitude foi aprobada e comecei a facer algunhas melloras no proxecto para preparalo para a súa presentación na conferencia.

Nese momento, o proxecto xa recibira un novo nome BIRMA, adquiriu varias capacidades adicionais (non tanto totalmente implementadas, senón asumidas) - todos os detalles pódense atopar no meu informe.

Para ser honesto, era difícil chamarlle a BIRMA 2013 algo completo; Sinceramente, foi unha manualidade moi hacky feita ás présas. En termos de código, practicamente non houbo innovacións especiais en absoluto, agás un intento bastante indefenso de crear algún tipo de sintaxe unificada para o analizador, que en aparencia lembra a linguaxe de formato IRBIS 64 (e de feito, tamén o sistema ISIS - con parénteses como estruturas cíclicas; por que Daquela pareceume moi chulo). O analizador tropezou irremediablemente con estes círculos de parénteses do tipo adecuado (xa que os parénteses tamén desempeñaban outro papel, é dicir, marcaban estruturas opcionais durante a análise que se poden omitir). Remítome de novo a todos os que queiran familiarizarse coa sintaxe daquela difícil de imaxinar e inxustificada de BIRMA con máis detalle ao meu informe daquela.

En xeral, ademais de loitar co meu propio analizador, non teño nada máis que dicir sobre o código desta versión, agás a conversión inversa das fontes existentes en C++ conservando algunhas características típicas do código .NET (para ser honesto, é difícil de entender , o que exactamente me impulsou a retroceder todo, probablemente algún medo estúpido por manter segredos os meus códigos fonte, como se fose algo equivalente á receita secreta de Coca-Cola).

Quizais esta decisión estúpida sexa tamén o motivo das dificultades para vincular a biblioteca DLL resultante coa interface existente dunha estación de traballo caseira para introducir datos nun catálogo electrónico (si, non mencionei outro dato importante: a partir de agora, todos o código do "motor" de BIRMA foi o esperado, está separado da parte da interface e empaquetado na DLL adecuada). Por que foi necesario escribir unha estación de traballo separada para estes fins, que de todos os xeitos, na súa aparencia e método de interacción co usuario, copiou sen vergoña a mesma estación de traballo "Catalogizer" do sistema IRBIS 64 - esta é unha pregunta separada. En resumo: deulle a solidez necesaria aos meus desenvolvementos daquela para o meu proxecto de graduación (se non, o motor analizador indixestíbel por si só non era suficiente). Ademais, entón atopei algunhas dificultades para implementar a interface da estación de traballo Cataloger cos meus propios módulos, implementados tanto en C++ como en C#, e acceder directamente ao meu motor.

En xeral, curiosamente, foi este prototipo bastante torpe do futuro BIRMA.NET o que estaba destinado a converterse no meu "cabalo de batalla" durante os próximos catro anos. Non se pode dicir que durante este tempo non tratei polo menos de buscar formas para unha implementación nova e máis completa dunha idea de longa data. Entre outras innovacións, xa debería haber secuencias cíclicas aniñadas que poderían incluír elementos opcionais; así é como ía dar vida á idea de modelos universais para descricións bibliográficas de publicacións e outras cousas interesantes. Porén, nas miñas actividades prácticas daquela, todo isto era pouco demandado, e a implantación que tiña naquel momento era bastante suficiente para introducir táboas de contidos. Ademais, a dirección de desenvolvemento da nosa biblioteca comezou a desviarse cada vez máis cara á dixitalización dos arquivos dos museos, a reportaxe e outras actividades de pouco interese para min, que ao final obrigou a abandonala por fin, dando paso aos que estar máis satisfeito con todo isto.

Paradoxalmente, foi despois destes dramáticos acontecementos cando o proxecto BIRMA, que daquela xa tiña todos os trazos característicos dun proxecto típico de construción a longo prazo, parecía comezar a cobrar a súa tan esperada nova vida! Tiven máis tempo libre para pensamentos ociosos, comecei de novo a peitear a World Wide Web en busca de algo semellante (afortunadamente, agora xa podía adiviñar buscar todo isto non en calquera lugar, senón en GitHub), e nalgún lugar de At the a principios deste ano, por fin atopeime cun produto correspondente da coñecida empresa Salesforce baixo o nome insignificante Gorp. Por si só, podería facer case todo o que necesitaba dun motor analizador deste tipo, é dicir, illar de xeito intelixente fragmentos individuais de texto arbitrario, pero claramente estruturado, ao tempo que tiña unha interface bastante amigable para o usuario final, incluíndo esencias tan comprensibles, como un patrón, modelo e ocorrencia, e ao mesmo tempo utilizando a sintaxe familiar das expresións regulares, que se fai incomparablemente máis lexible debido á división en grupos semánticos designados para a análise.

En xeral, decidín que este é o único Gorp (Pregúntome que significa este nome? Quizais algún tipo de "analizador regular orientado xeralmente"?) - exactamente o que estiven buscando durante moito tempo. É certo que a súa implementación inmediata para as miñas propias necesidades tiña tal problema que este motor requiría un cumprimento demasiado estricto da secuencia estrutural do texto fonte. Para algúns informes como os ficheiros de rexistro (é dicir, foron colocados polos desenvolvedores como exemplos claros de uso do proxecto), isto é bastante axeitado, pero para os mesmos textos de táboas de contido dixitalizadas, é improbable. Despois de todo, a mesma páxina cunha táboa de contidos pode comezar coas palabras "Índice", "Índice" e calquera outra descrición preliminar que non necesitemos colocar nos resultados da análise prevista (e cortándoas manualmente). cada vez tamén é inconveniente). Ademais, entre elementos repetitivos individuais, como o nome do autor, o título e o número de páxina, a páxina pode conter unha certa cantidade de lixo (por exemplo, debuxos e só caracteres aleatorios), que tamén sería bo poder cortar. Non obstante, o último aspecto aínda non era tan significativo, pero debido ao primeiro, a implantación existente non puido comezar a buscar as estruturas necesarias no texto desde un lugar determinado, senón que simplemente o procesou desde o principio, non atopou o especificou patróns alí e... rematou o meu traballo. Obviamente, facía falta algún axuste para polo menos permitir un espazo entre as estruturas que se repiten, e iso volveume a traballar.

Outro problema foi que o proxecto en si estaba implementado en Java, e se planeaba implementar no futuro algún medio para interconectar esta tecnoloxía con aplicacións coñecidas para introducir datos nas bases de datos existentes (como o "Cataloguer") de Irbis, polo menos polo menos. fai isto en C# e .NET. Non se trata de que Java en si sexa unha mala linguaxe: ata unha vez useino para implementar unha interesante aplicación de fiestra que implementaba a funcionalidade dunha calculadora programable doméstica (como parte dun proxecto de curso). E en termos de sintaxe é moi semellante ao mesmo C sostenido. Pois isto é só unha vantaxe: máis doado me será finalizar un proxecto existente. Non obstante, non quería mergullarme de novo neste mundo bastante inusual das tecnoloxías Java de fiestras (ou, mellor dito, de escritorio). a experiencia anterior. Quizais sexa precisamente porque C# en conxunto con WinForms está moito máis preto de Delphi, co que moitos de nós comezamos. Afortunadamente, a solución necesaria atopouse con bastante rapidez - en forma de proxecto IKVM.NET, o que facilita a tradución dos programas Java existentes en código .NET xestionado. É certo que o proxecto en si xa fora abandonado polos autores nese momento, pero a súa última implementación permitiu levar a cabo con bastante éxito as accións necesarias para os textos fonte. Gorp.

Así que fixen todos os cambios necesarios e montei todo nunha DLL do tipo apropiado, que podería ser facilmente "recollida" por calquera proxecto para o .NET Framework creado en Visual Studio. Mentres tanto, creei outra capa para a presentación cómoda dos resultados devoltos Gorp, en forma de estruturas de datos correspondentes que sería conveniente procesar nunha vista de táboa (tomando como base tanto filas como columnas; tanto claves de dicionario como índices numéricos). Ben, as propias utilidades necesarias para procesar e mostrar os resultados foron escritas con bastante rapidez.

Ademais, o proceso de adaptación de modelos para o novo motor para ensinarlle a analizar mostras existentes de textos escaneados de táboas de contidos non causou complicacións especiais. De feito, nin sequera tiven que referirme aos meus modelos anteriores: simplemente creei todos os modelos necesarios desde cero. Ademais, se os modelos deseñados para funcionar coa versión anterior do sistema establecen un marco bastante estreito para os textos que poderían ser analizados correctamente coa súa axuda, o novo motor xa permitía desenvolver modelos bastante universais axeitados para varios tipos de marcado en unha vez. Incluso tentei escribir algún tipo de modelo completo para calquera texto de índice arbitrario, aínda que, por suposto, aínda que se me abrían todas as novas posibilidades, incluíndo, en particular, a limitada capacidade de implementar as mesmas secuencias repetidas aniñadas ( como, por exemplo, apelidos e iniciais varios autores seguidos), isto resultou ser unha utopía.

Quizais no futuro sexa posible implementar un determinado concepto de metamodelos, que poderá comprobar o texto de orixe para o cumprimento de varios dos modelos dispoñibles á vez e, a continuación, de acordo cos resultados obtidos, seleccionar o o máis adecuado, usando algún tipo de algoritmo intelixente. Pero agora estaba máis preocupado por outra pregunta. Un analizador como Gorp, a pesar de toda a súa versatilidade e as modificacións que fixen, aínda era inherentemente incapaz de facer unha cousa aparentemente sinxela que o meu analizador autoescrito foi capaz de facer desde a primeira versión. A saber: tiña a capacidade de atopar e extraer do texto fonte todos os fragmentos que coincidan coa máscara especificada dentro do modelo empregado no lugar correcto, sen interesarlle en absoluto o que contén o texto dado nos espazos entre estes fragmentos. Ata agora, só mellorei lixeiramente o novo motor, permitíndolle buscar todas as posibles novas repeticións dunha secuencia determinada de tales máscaras desde a posición actual, deixando a posibilidade de que no texto aparezan conxuntos de caracteres arbitrarios que estaban completamente non contabilizados na análise, encerrados entre as estruturas repetidas detectadas. Non obstante, isto non permitiu establecer a seguinte máscara independentemente dos resultados da busca do fragmento anterior mediante a máscara correspondente: a rigorosidade da estrutura do texto descrita aínda non deixaba espazo para inclusións arbitrarias de caracteres irregulares.

E se para os exemplos de táboas de contido que atopei, este problema aínda non parecía tan grave, entón ao intentar aplicar un novo mecanismo de análise a unha tarefa similar de analizar o contido dun sitio web (é dicir, a mesma análise), é necesario. limitacións están aquí apareceron con toda a súa obviedade. Despois de todo, é bastante sinxelo establecer as máscaras necesarias para os fragmentos de marcado web, entre os que deberían situarse os datos que buscamos (que hai que extraer), pero como podemos obrigar ao analizador a pasar inmediatamente ao seguinte fragmento semellante, a pesar de todas as posibles etiquetas e atributos HTML que se poden colocar nos espazos entre eles?

Despois de pensar un pouco, decidín introducir un par de patróns de servizo (%all_before) и (%all_after), cumprindo o obvio propósito de garantir que todo o que poida estar contido no texto fonte se salte antes de calquera patrón (máscara) que os siga. Ademais, se (%all_before) simplemente ignorou todas estas inclusións arbitrarias, entón (%all_after), pola contra, permitiu engadirlles ao fragmento desexado despois de pasar do fragmento anterior. Parece ben sinxelo, pero para implementar este concepto tiven que peitear de novo as fontes gorp para facer as modificacións necesarias para non romper a lóxica xa implementada. Ao final, conseguimos facelo (aínda que a primeira implementación do meu analizador, aínda que con erros, foi escrita, e aínda máis rápido, nun par de semanas). A partir de agora, o sistema adquiriu unha forma verdadeiramente universal, nada menos que 12 anos despois dos primeiros intentos de facelo funcionar.

Por suposto, este non é o final dos nosos soños. Tamén pode reescribir completamente o analizador de modelos gorp en C#, usando calquera das bibliotecas dispoñibles para implementar unha gramática libre. Creo que o código debería simplificarse significativamente, e isto permitiranos desfacernos do legado en forma de fontes Java existentes. Pero co tipo de motor existente, tamén é moi posible facer varias cousas interesantes, incluíndo un intento de implementar os metamodelos que xa mencionei, por non falar de analizar varios datos de varios sitios web (porén, non descarto que as ferramentas de software especializada existentes son máis axeitadas para iso; simplemente non tiven a experiencia adecuada de usalas aínda).

Por certo, este verán xa recibín unha invitación por correo electrónico dunha empresa que utiliza tecnoloxías de Salesforce (o desenvolvedor do orixinal Gorp), aprobar unha entrevista para o traballo posterior en Riga. Por desgraza, polo momento non estou preparado para este tipo de redistribucións.

Se este material esperta algún interese, na segunda parte intentarei describir con máis detalle a tecnoloxía para compilar e posteriormente analizar modelos utilizando o exemplo da implementación utilizada en Salesforce. Gorp (as miñas propias adicións, coa excepción dun par de palabras de función xa descritas, practicamente non fan cambios na sintaxe do modelo en si, polo que case toda a documentación para o sistema orixinal Gorp Adecuado tamén para a miña versión).

Fonte: www.habr.com

Engadir un comentario