A história de um pequeno projeto de doze anos (sobre a BIRMA.NET pela primeira vez e francamente em primeira mão)

O nascimento deste projecto pode ser considerado uma pequena ideia que me surgiu algures no final de 2007, que estava destinada a encontrar a sua forma final apenas 12 anos depois (neste momento - claro, embora a implementação actual, segundo para o autor, é muito satisfatório).

Tudo começou quando, no processo de cumprimento das minhas funções oficiais na biblioteca, chamei a atenção para o facto de o processo de introdução de dados do texto digitalizado de índices de publicações de livros (e musicais) na base de dados existente, aparentemente, pode ser significativamente simplificado e automatizado, aproveitando a propriedade de ordem e repetibilidade de todos os dados necessários para entrada, como o nome do autor do artigo (se estivermos falando de uma coleção de artigos), o título do o artigo (ou o subtítulo refletido no índice) e o número da página do item atual do índice. A princípio fiquei praticamente convencido de que um sistema adequado para realizar esta tarefa poderia ser facilmente encontrado na Internet. Quando alguma surpresa foi causada pelo fato de não ter conseguido encontrar tal projeto, decidi tentar implementá-lo sozinho.

Depois de pouco tempo começou a funcionar o primeiro protótipo, que comecei imediatamente a utilizar nas minhas atividades diárias, depurando-o simultaneamente em todos os exemplos que me vieram à mão. Felizmente, no meu local de trabalho habitual, onde eu não era de forma alguma um programador, ainda consegui escapar de um visível “tempo de inatividade” no meu trabalho, durante o qual depurei intensamente minha ideia - algo quase impensável nas realidades atuais, que implicam relatórios diários sobre o trabalho realizado durante o dia. O processo de aperfeiçoamento do programa demorou um total de pelo menos cerca de um ano, mas mesmo depois disso o resultado dificilmente poderia ser considerado completamente bem sucedido - foram inicialmente estabelecidos demasiados conceitos diferentes que não eram totalmente claros para implementação: elementos opcionais que podem ser ignorado; visualização antecipada de elementos (com a finalidade de substituir elementos anteriores nos resultados da pesquisa); até mesmo nossa própria tentativa de implementar algo como expressões regulares (que possuem uma sintaxe única). Devo dizer que antes disso eu havia desistido um pouco da programação (por cerca de 8 anos, se não mais), então a nova oportunidade de aplicar minhas habilidades em uma tarefa interessante e necessária capturou completamente minha atenção. Não é surpreendente que o código-fonte resultante - na ausência de qualquer abordagem clara ao seu design da minha parte - rapidamente se tornou uma mistura inimaginável de peças díspares na linguagem C com alguns elementos de C++ e aspectos de programação visual (inicialmente era foi decidido usar um sistema de design como o Borland C++ Builder - “quase Delphi, mas em C”). Porém, tudo isso acabou rendendo frutos na automatização das atividades diárias de nossa biblioteca.

Ao mesmo tempo, decidi, por precaução, fazer cursos para formar desenvolvedores profissionais de software. Não sei se é realmente possível aprender “ser programador” do zero lá, mas levando em conta as habilidades que eu já tinha naquela época, consegui dominar um pouco tecnologias que eram mais relevantes naquela época, como como C#, Visual Studio para desenvolvimento em .NET, além de algumas tecnologias relacionadas a Java, HTML e SQL. Toda a formação durou dois anos e serviu de ponto de partida para outro projeto meu, que se estendeu por vários anos - mas este é um tema para uma publicação separada. Aqui seria apenas apropriado notar que fiz uma tentativa de adaptar os desenvolvimentos que já tinha no projeto descrito para criar uma aplicação de janela completa em C# e WinForms que implemente as funcionalidades necessárias, e usá-la como base para o próximo projeto de diploma.
Com o tempo, esta ideia começou a parecer-me digna de ser expressa em conferências anuais com a participação de representantes de várias bibliotecas como “LIBKOM” e “CRIMEA”. A ideia, sim, mas não a minha implementação naquela época. Então eu também esperava que alguém o reescrevesse usando abordagens mais competentes. De uma forma ou de outra, em 2013 decidi escrever um relatório sobre o meu trabalho preliminar e enviá-lo à Comissão Organizadora da Conferência com um pedido de bolsa para participação na conferência. Para minha certa surpresa, minha inscrição foi aprovada e comecei a fazer algumas melhorias no projeto para prepará-lo para apresentação na conferência.

Naquela época, o projeto já havia recebido um novo nome BIRMA, adquirido vários recursos adicionais (não tanto totalmente implementados, mas sim assumidos) - todos os detalhes podem ser encontrados no meu relatório.

Para ser sincero, foi difícil chamar a BIRMA 2013 de algo completo; Falando francamente, foi uma arte muito hackeada, feita às pressas. Em termos de código, praticamente não houve nenhuma inovação especial, exceto uma tentativa um tanto indefesa de criar algum tipo de sintaxe unificada para o analisador, que na aparência lembra a linguagem de formatação IRBIS 64 (e na verdade, também o sistema ISIS - com parênteses como estruturas cíclicas; por que Na época achei muito legal). O analisador tropeçou irremediavelmente nesses círculos de parênteses do tipo apropriado (já que os parênteses também desempenharam outra função, ou seja, marcaram estruturas opcionais durante a análise que podem ser ignoradas). Remeto novamente a todos que desejam se familiarizar com a sintaxe então difícil de imaginar e injustificada da BIRMA com mais detalhes ao meu relatório da época.

Em geral, além de lutar com meu próprio analisador, não tenho mais nada a dizer sobre o código desta versão - exceto a conversão reversa das fontes existentes para C++, preservando algumas características típicas do código .NET (para ser honesto, é difícil de entender, o que exatamente me levou a recuar tudo - provavelmente algum medo estúpido de manter meus códigos-fonte em segredo, como se fosse algo equivalente à receita secreta da Coca-Cola).

Talvez essa decisão estúpida também seja a razão das dificuldades em emparelhar a biblioteca DLL resultante com a interface existente de uma estação de trabalho caseira para entrada de dados em um catálogo eletrônico (sim, não mencionei outro fato importante: a partir de agora, todos o código do “motor” BIRMA ficou dentro do esperado, é separado da parte de interface e empacotado na DLL apropriada). Por que foi necessário escrever uma estação de trabalho separada para esses fins, que de qualquer maneira, em sua aparência e método de interação com o usuário, copiou descaradamente a mesma estação de trabalho “Catalogizador” do sistema IRBIS 64 - esta é uma questão à parte. Resumindo: deu a solidez necessária aos meus desenvolvimentos para o meu projeto de graduação (caso contrário, o indigesto motor do analisador por si só não era suficiente). Além disso, encontrei então algumas dificuldades em implementar a interface da estação de trabalho do Catalogador com módulos próprios, implementados tanto em C++ quanto em C#, e acessar diretamente meu motor.

Em geral, curiosamente, foi esse protótipo um tanto desajeitado do futuro BIRMA.NET que estava destinado a se tornar meu “burro de carga” pelos próximos quatro anos. Não se pode dizer que durante este tempo não tentei pelo menos encontrar caminhos para uma implementação nova e mais completa de uma ideia de longa data. Entre outras inovações, já deveriam existir sequências cíclicas aninhadas que poderiam incluir elementos opcionais - era assim que eu ia dar vida à ideia de modelos universais para descrições bibliográficas de publicações e várias outras coisas interessantes. Porém, nas minhas atividades práticas da época, tudo isso era pouco procurado, e a implementação que eu tinha naquela época era suficiente para inserir índices. Além disso, o vetor de desenvolvimento da nossa biblioteca começou a desviar-se cada vez mais para a digitalização dos arquivos museológicos, reportagens e outras atividades de pouco interesse para mim, o que acabou por me obrigar a abandoná-la definitivamente, dando lugar a quem iria fique mais satisfeito com tudo isso.

Paradoxalmente, foi depois destes acontecimentos dramáticos que o projecto BIRMA, que naquela altura já tinha todas as características de um típico projecto de construção de longo prazo, pareceu começar a ganhar a sua tão esperada nova vida! Tive mais tempo livre para pensamentos ociosos, comecei novamente a vasculhar a World Wide Web em busca de algo semelhante (felizmente, agora já consegui adivinhar como procurar tudo isso não apenas em qualquer lugar, mas no GitHub), e em algum lugar no No no início deste ano, finalmente encontrei um produto correspondente da conhecida empresa Salesforce com o nome insignificante Gorp.. Por si só, ele poderia fazer quase tudo o que eu precisava desse mecanismo de analisador - ou seja, isolar inteligentemente fragmentos individuais de texto arbitrário, mas claramente estruturado, ao mesmo tempo em que tinha uma interface bastante amigável para o usuário final, incluindo essências compreensíveis, como um padrão, modelo e ocorrência, e ao mesmo tempo usando a sintaxe familiar de expressões regulares, que se torna incomparavelmente mais legível devido à divisão em grupos semânticos designados para análise.

Em geral, decidi que este é o único Gorp. (Eu me pergunto o que esse nome significa? Talvez algum tipo de “analisador regular de orientação geral”?) – exatamente o que procuro há muito tempo. É verdade que a sua implementação imediata para as minhas próprias necessidades teve um problema tão grande que este motor exigia uma adesão demasiado estrita à sequência estrutural do texto fonte. Para alguns relatórios, como arquivos de log (ou seja, eles foram colocados pelos desenvolvedores como exemplos claros de uso do projeto), isso é bastante adequado, mas para os mesmos textos de índices digitalizados, é improvável. Afinal, a mesma página com índice pode começar com as palavras “Índice”, “Conteúdo” e quaisquer outras descrições preliminares que não precisamos colocar nos resultados da análise pretendida (e cortá-los manualmente cada vez também é inconveniente). Além disso, entre elementos individuais repetidos, como nome do autor, título e número da página, a página pode conter uma certa quantidade de lixo (por exemplo, desenhos e apenas caracteres aleatórios), que também seria bom poder cortar. Porém, o último aspecto ainda não era tão significativo, mas devido ao primeiro, a implementação existente não pôde começar a procurar as estruturas necessárias no texto a partir de um determinado local, mas simplesmente processou-o desde o início, não encontrou o especifiquei padrões lá e... encerrei meu trabalho. Obviamente, foram necessários alguns ajustes para permitir pelo menos algum espaço entre as estruturas repetidas, e isso me fez voltar ao trabalho.

Outro problema foi que o projeto em si foi implementado em Java, e se eu planejasse no futuro implementar algum meio de interface dessa tecnologia com aplicativos familiares para inserir dados em bancos de dados existentes (como o “Catálogo” da Irbis), então pelo menos pelo menos faça isso em C# e .NET. Não é que o Java em si seja uma linguagem ruim – uma vez até o usei para implementar um aplicativo de janela interessante que implementava a funcionalidade de uma calculadora programável doméstica (como parte de um projeto de curso). E em termos de sintaxe é muito semelhante ao mesmo dó sustenido. Bem, isso é apenas uma vantagem: mais fácil será para mim finalizar um projeto existente. No entanto, eu não queria mergulhar novamente neste mundo bastante incomum de tecnologias Java de janela (ou melhor, desktop) - afinal, a linguagem em si não foi “adaptada” para tal uso, e eu não ansiava de forma alguma por uma repetição de a experiência anterior. Talvez seja precisamente porque o C# em conjunto com o WinForms está muito mais próximo do Delphi, com o qual muitos de nós começamos. Felizmente, a solução necessária foi encontrada rapidamente - na forma do projeto IKVM.NET, o que facilita a tradução de programas Java existentes em código .NET gerenciado. É verdade que o projeto em si já havia sido abandonado pelos autores naquela época, mas sua última implementação permitiu-me realizar com bastante sucesso as ações necessárias para os textos de origem Gorp..

Então fiz todas as alterações necessárias e montei tudo em uma DLL do tipo apropriado, que poderia facilmente ser “captada” por qualquer projeto para o .NET Framework criado no Visual Studio. Nesse ínterim, criei outra camada para apresentação conveniente dos resultados retornados Gorp., na forma de estruturas de dados correspondentes que seriam convenientes para processar em uma visualização de tabela (tomando como base linhas e colunas; chaves de dicionário e índices numéricos). Bem, os próprios utilitários necessários para processar e exibir os resultados foram escritos rapidamente.

Além disso, o processo de adaptação de modelos para o novo mecanismo, a fim de ensiná-lo a analisar amostras existentes de textos digitalizados de índices não causou complicações especiais. Na verdade, nem precisei consultar meus modelos anteriores: simplesmente criei todos os modelos necessários do zero. Além disso, se os modelos concebidos para funcionar com a versão anterior do sistema estabeleciam uma estrutura bastante estreita para textos que poderiam ser analisados ​​corretamente com a sua ajuda, o novo motor já permitia desenvolver modelos bastante universais adequados para vários tipos de marcação em uma vez. Eu até tentei escrever algum tipo de modelo abrangente para qualquer texto de índice arbitrário, embora, é claro, mesmo com todas as novas possibilidades que se abrem para mim, incluindo, em particular, a capacidade limitada de implementar as mesmas sequências repetidas aninhadas ( como, por exemplo, sobrenomes e iniciais de vários autores seguidos), isso acabou sendo uma utopia.

Talvez no futuro seja possível implementar um determinado conceito de meta-modelos, que será capaz de verificar a conformidade do texto fonte com vários dos modelos disponíveis de uma só vez, e então, de acordo com os resultados obtidos, selecionar o mais adequado, usando algum tipo de algoritmo inteligente. Mas agora eu estava mais preocupado com outra questão. Um analisador como Gorp., apesar de toda a sua versatilidade e das modificações que fiz, ele ainda era inerentemente incapaz de fazer algo aparentemente simples que meu analisador escrito por ele mesmo foi capaz de fazer desde a primeira versão. A saber: ele tinha a capacidade de encontrar e extrair do texto fonte todos os fragmentos que correspondiam à máscara especificada no modelo usado no lugar certo, sem estar nem um pouco interessado no que o texto fornecido contém nos espaços entre esses fragmentos. Até agora, melhorei apenas ligeiramente o novo motor, permitindo-lhe procurar todas as novas repetições possíveis de uma determinada sequência de tais máscaras a partir da posição actual, deixando a possibilidade da presença no texto de conjuntos de caracteres arbitrários que foram completamente não contabilizado na análise, colocado entre as estruturas repetidas detectadas. No entanto, isso não permitiu definir a próxima máscara independentemente dos resultados da busca do fragmento anterior utilizando a máscara correspondente: o rigor da estrutura do texto descrita ainda não deixava espaço para inclusões arbitrárias de caracteres irregulares.

E se para os exemplos de índices que encontrei este problema ainda não parecia tão sério, então ao tentar aplicar um novo mecanismo de análise a uma tarefa semelhante de análise do conteúdo de um site (ou seja, a mesma análise), seu limitações estão aqui, elas apareceram com toda a sua obviedade. Afinal, é muito fácil definir as máscaras necessárias para fragmentos de marcação web, entre os quais devem estar localizados os dados que procuramos (que precisam ser extraídos), mas como podemos forçar o analisador a passar imediatamente para o próximo fragmento semelhante, apesar de todas as tags e atributos HTML possíveis que podem ser colocados nos espaços entre eles?

Depois de pensar um pouco, decidi apresentar alguns padrões de serviço (%todos_antes) и (%todos_depois), servindo ao propósito óbvio de garantir que tudo o que possa estar contido no texto fonte seja ignorado antes de qualquer padrão (máscara) que os siga. Além disso, se (%todos_antes) simplesmente ignorou todas essas inclusões arbitrárias, então (%todos_depois), pelo contrário, permitiu que eles fossem adicionados ao fragmento desejado após passarem do fragmento anterior. Parece bastante simples, mas para implementar este conceito tive que vasculhar novamente as fontes gorp para fazer as modificações necessárias para não quebrar a lógica já implementada. No final, conseguimos fazer isso (embora até a primeira, embora com muitos bugs, implementação do meu analisador tenha sido escrita, e ainda mais rápido - em algumas semanas). A partir de agora, o sistema assumiu uma forma verdadeiramente universal - nada menos que 12 anos após as primeiras tentativas de fazê-lo funcionar.

Claro, este não é o fim dos nossos sonhos. Você também pode reescrever completamente o analisador de modelo gorp em C#, usando qualquer uma das bibliotecas disponíveis para implementar uma gramática livre. Acho que o código deveria ser significativamente simplificado, e isso nos permitirá livrar-nos do legado na forma de fontes Java existentes. Mas com o tipo de motor existente, também é possível fazer várias coisas interessantes, incluindo uma tentativa de implementar os meta-templates que já mencionei, sem falar na análise de vários dados de vários sites (no entanto, não descarto que as ferramentas de software especializadas existentes são mais adequadas para isso – só não tive a experiência adequada de usá-las ainda).

Aliás, neste verão já recebi um convite por email de uma empresa que utiliza tecnologias Salesforce (a desenvolvedora do original Gorp.), passar uma entrevista para posterior trabalho em Riga. Infelizmente, no momento não estou pronto para tais reafectações.

Se este material despertar algum interesse, então na segunda parte tentarei descrever com mais detalhes a tecnologia de compilação e posterior análise de templates usando o exemplo da implementação usada no Salesforce Gorp. (as minhas próprias adições, com a excepção de algumas palavras funcionais já descritas, não fazem praticamente nenhuma alteração à sintaxe do modelo em si, por isso quase toda a documentação do sistema original Gorp. Adequado para a minha versão também).

Fonte: habr.com

Adicionar um comentário