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

O nascimento deste projeto pode ser considerado uma pequena idéia que me visitou em algum lugar no final de 2007, que estava destinada a encontrar sua forma final apenas 12 anos depois (neste momento - é claro, embora a implementação atual, na opinião do autor, seja muito satisfatória) .

Tudo começou com o fato de que, no processo de cumprir minhas obrigações oficiais na biblioteca, chamei a atenção para o fato de que o processo de inserção de dados do texto digitalizado do índice do conteúdo das publicações de livros (e músicas) no banco de dados existente, aparentemente, pode ser bastante simplificado e automatizar, usando a propriedade de ordem e repetibilidade de todos os dados necessários para a entrada, como o nome do autor do artigo (se estamos falando de uma coleção de artigos), o nome do artigo (ou a legenda refletida no índice) e A página do índice atual. No começo, eu estava quase convencido de que um sistema adequado para essa tarefa poderia ser facilmente encontrado na Internet. Quando alguma surpresa foi causada pelo fato de eu não encontrar esse projeto, decidi tentar implementá-lo por conta própria.

Depois de um tempo relativamente curto, o primeiro protótipo começou a funcionar, que eu imediatamente comecei a usar em minhas atividades diárias, depurando-o simultaneamente com todos os exemplos que vieram à minha mão. Felizmente, no meu local de trabalho habitual, onde eu não era de forma alguma um programador, ainda conseguia me livrar de "períodos de inatividade" visíveis no trabalho, durante os quais trabalhei duro para depurar minha ideia - algo quase impensável nas realidades de hoje, implicando relatórios diários sobre o trabalho realizado durante o dia. O processo de polimento do programa levou um total de não menos de um ano, mas mesmo depois disso o resultado dificilmente poderia ser considerado bem-sucedido - havia muitos conceitos diferentes que não eram muito inteligíveis para implementar desde o início: elementos opcionais que poderiam ser ignorados; visualização principal de elementos (com o objetivo de substituir os resultados da pesquisa de elementos anteriores); até sua própria tentativa de implementar algo como expressões regulares (com uma sintaxe distinta). Devo dizer que antes disso eu consegui jogar um pouco a programação (por cerca de 8 anos, se não mais), então uma nova oportunidade de aplicar minhas habilidades a uma tarefa interessante e necessária capturou completamente minha atenção. Não é de surpreender que o código fonte resultante - na ausência de abordagens inteligíveis para projetá-lo para mim - rapidamente se tornou uma mistura inimaginável de peças díspares na linguagem C com alguns elementos C ++ e aspectos da programação visual (foi originalmente decidido usar um sistema de design como o Borland C ++ Builder - "quase Delphi, mas em C"). No entanto, tudo isso acabou valendo a pena na automação das atividades diárias da nossa biblioteca.

Ao mesmo tempo, decidi, por precaução, fazer cursos de treinamento para desenvolvedores profissionais de software. Não sei se é possível realmente aprender "de um programador" do zero, mas, levando em conta as habilidades que eu já tinha na época, consegui dominar tecnologias um pouco mais avançadas, como o C #, Visual Studio para desenvolvimento em baixo. NET, bem como algumas das tecnologias relacionadas a Java, HTML e SQL. Todo o treinamento levou um total de dois anos e serviu como ponto de partida para outro dos meus projetos, que se estenderam por vários anos - mas esse já é um tópico para uma publicação separada. Aqui, será pertinente observar que fiz uma tentativa de adaptar a experiência que já tinha no projeto descrito para criar um aplicativo de janela completo em C # e WinForms que implementa a funcionalidade necessária e colocá-lo na base do próximo projeto de graduação.
Com o tempo, essa idéia começou a parecer digna de ser expressa em tais conferências anuais com a participação de representantes de várias bibliotecas, como LIBCOM e CRIMEA. A idéia é sim, mas de maneira alguma a minha realização daquele tempo. Também esperava, entre outras coisas, que alguém o reescrevesse usando abordagens mais competentes. De uma forma ou de outra, até 2013, decidi elaborar um relatório sobre meu trabalho preliminar e enviá-lo ao Comitê Organizador da Conferência com um pedido de subsídio para participar da conferência. Para minha surpresa, minha inscrição foi atendida 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árias oportunidades adicionais (não tão plenamente realizadas quanto as previstas) - todos os detalhes podem ser encontrados no meu relatório .

Francamente, a BIRMA de 2013 foi difícil de chamar de algo completo; francamente, era um chicote feito de artesanato muito hacky. Quanto à parte do código, praticamente não houve inovações especiais, além da tentativa indefesa de criar algum tipo de sintaxe unificada para o analisador, que na aparência se assemelha à linguagem de formatação IRBIS 64 (e, de fato, até ao ISIS, com parênteses no papel de estruturas cíclicas; por que então me pareceu muito legal). O analisador tropeçou irremediavelmente nessas banheiras de hidromassagem dos suportes do tipo correspondente (uma vez que os parênteses tiveram o mesmo papel ali, ou seja, eles marcaram estruturas opcionais que poderiam ser puladas durante a análise). Todo mundo que quer se familiarizar com mais detalhes com a sintaxe BIRMA, então difícil de imaginar e injustificada, refiro-me novamente ao meu relatório da época.

Em geral, exceto pela luta com nosso próprio analisador, então, no que diz respeito ao código desta versão, não tenho mais nada a dizer - exceto a conversão reversa das fontes disponíveis em C ++ com a preservação de alguns recursos típicos do código .NET (para ser honesto, é difícil entender o que exatamente me levou a transferir tudo de volta - provavelmente algum tipo de medo maluco 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 contenha o motivo das dificuldades em emparelhar a DLL resultante com a interface existente da estação de trabalho para inserir dados no catálogo eletrônico (sim, eu não mencionei outro fato importante: a partir de agora todo o código do mecanismo BIRMA era conforme o esperado, separado da interface e empacotado na DLL apropriada). Por que você precisou escrever uma estação de trabalho separada para esses fins, que de qualquer maneira, em sua aparência e na maneira de interagir com o usuário, copiava descaradamente a mesma estação de trabalho “Catalogadora” do sistema IRBIS 64 - esse é um problema separado. Resumindo: ele deu o devido respeito às minhas realizações daquele tempo no projeto de graduação (caso contrário, o mecanismo de análise indigestível por si só não era suficiente). Além disso, encontrei algumas dificuldades ao implementar o emparelhamento da estação de trabalho “Catalogizer” com meus próprios módulos implementados em C ++ e C # e ao endereçar diretamente ao meu mecanismo.

Em geral, por incrível que pareça, mas foi esse protótipo bastante estranho do futuro BIRMA.NET que estava destinado a se tornar meu "cavalo de batalha" pelos próximos quatro anos. Não se pode dizer que, durante esse período, nem tentei encontrar maneiras de uma implementação nova e mais completa de uma idéia de longa data. Entre outras inovações, já deveria ter havido sequências cíclicas aninhadas, que também poderiam incluir elementos opcionais - era assim que eu realizaria a ideia de modelos universais para descrição bibliográfica de publicações e várias outras coisas interessantes. No entanto, na minha prática naquela época, tudo isso era pouco exigido, e a implementação que eu tinha naquele momento foi suficiente para introduzir o índice. Além disso, o vetor da direção de desenvolvimento de nossa biblioteca começou a se desviar cada vez mais para digitalizar os arquivos dos museus, gerando relatórios e outras atividades que eram de pouco interesse para mim, o que no final me fez abandoná-lo completamente, dando lugar a quem ficaria mais satisfeito com tudo isso .

Paradoxalmente, mas precisamente após esses eventos dramáticos, o projeto BIRMA, que na época já possuía todas as características de uma construção típica de longo prazo, parecia começar a ganhar sua tão esperada nova vida! Eu tinha mais tempo livre para pensamentos ociosos, comecei a vasculhar a World Wide Web em busca de algo semelhante (bom, agora eu já podia adivinhar procurar tudo isso em qualquer lugar, principalmente no GitHub), e em algum lugar No início deste ano, finalmente encontrei o ofício correspondente do conhecido escritório da Salesforce sob o nome sem importância Gorp . Por si só, ele poderia fazer quase tudo o que eu precisava de um mecanismo desse analisador - ou seja, isolar inteligentemente fragmentos individuais de um arbitrário, mas com uma estrutura clara do texto, além de ter uma interface bastante digerível para o usuário final, incluindo informações claras entidades como padrão, padrão e ocorrência e, ao mesmo tempo, envolvendo a sintaxe usual de expressões regulares, que se torna incomparavelmente mais legível em virtude da divisão em grupos semânticos significativos para análise.

Em geral, eu decidi que esse mesmo Gorp (eu me pergunto o que esse nome significa? Talvez algum "analisador regular de orientação geral"?) É exatamente o que estou procurando há muito tempo. É verdade que sua implementação imediata para minhas próprias necessidades tinha um problema tão grande que esse mecanismo exigia uma adesão muito estrita à sequência estrutural do texto de origem. Para alguns relatórios, como arquivos de log (ou seja, eles foram colocados pelos desenvolvedores como exemplos visuais do uso do projeto), isso funcionará bem, mas para os mesmos textos o índice digitalizado é improvável. Afinal, a mesma página com o índice pode começar com as palavras "Índice", "Conteúdo" e algumas outras descrições preliminares que não precisamos incluir nos resultados da análise proposta (e também é inconveniente cortá-los manualmente a cada vez). Além disso, entre elementos repetidos individuais, como o nome do autor, título e número da página, a página pode conter uma certa quantidade de lixo (por exemplo, figuras e apenas caracteres aleatórios), o que também seria bom para poder ser cortado. No entanto, o último aspecto ainda não era tão significativo, mas em virtude do primeiro, a implementação existente não pôde começar a procurar as estruturas necessárias no texto a partir de algum local específico, mas apenas processou-o desde o início, não encontrou os padrões especificados lá e ... terminou seu trabalho. Obviamente, era necessária uma revisão apropriada, que permitisse pelo menos deixar algumas lacunas entre as estruturas repetidas, e isso me fez sentar novamente no trabalho.

Outro problema foi que o próprio projeto foi implementado em Java, e se eu planejava implementar alguns meios de interface dessa tecnologia com os aplicativos usuais para inserir dados em bancos de dados existentes (como o catalogador Irbis), pelo menos pelo menos faça isso em C # e .NET. Não que o Java em si fosse uma linguagem ruim - uma vez que eu mesmo implementei um aplicativo de janela desinteressante que implementa a funcionalidade de uma calculadora programável doméstica (como parte de um projeto de curso). Sim, e na sintaxe é muito semelhante ao mesmo C-sharpe. Bem, isso é apenas uma vantagem: mais fácil será para mim finalizar um projeto existente. No entanto, eu não queria mergulhar nesse mundo incomum de tecnologias Java de janela (ou melhor, de desktop) - no final, a linguagem em si não foi "aprimorada" para esse uso, e não desejei repetir a experiência anterior. Talvez seja porque o C # em conjunto com o WinForms esteja muito mais próximo do Delphi, que muitos de nós iniciamos uma vez. Felizmente, a solução certa foi encontrada rapidamente - na pessoa do projeto IKVM.NET , que facilita a conversão de programas Java existentes em código .NET gerenciado. É verdade que o projeto em si já havia sido abandonado pelos autores na época, mas sua implementação mais recente me permitiu executar com êxito as ações necessárias para os textos originais do Gorp .

Então, fiz todas as alterações necessárias e coloquei tudo em uma DLL do tipo apropriado, que qualquer projeto do .NET Framework criado no Visual Studio poderia facilmente "pegar". Enquanto isso , criei outra camada para uma apresentação conveniente dos resultados retornados pelo Gorp , na forma de estruturas de dados correspondentes que seriam convenientes para processar em uma representação de tabela (e tendo 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 gravados rapidamente.

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

É possível que, no futuro, seja possível implementar um certo conceito de metamodelos que possam verificar o texto de origem quanto à conformidade com vários modelos disponíveis ao mesmo tempo e, em seguida, de acordo com os resultados obtidos, escolher o mais adequado usando algum algoritmo inteligente. Mas agora eu estava mais preocupado com outra pergunta. Um analisador como o Gorp , apesar de toda a versatilidade e modificações feitas por mim, ainda era, por natureza, incapaz de executar uma coisa aparentemente simples que meu analisador manuscrito conseguiu fazer desde a primeira versão. Nomeadamente: ele tinha a capacidade de encontrar e extrair do texto de origem todos os fragmentos que correspondam à máscara especificada na estrutura do modelo usado no lugar certo, embora nem um pouco interessado no que o texto contém nos espaços entre esses fragmentos. Até agora, aprimorei apenas ligeiramente o novo mecanismo, permitindo pesquisar todas as novas repetições possíveis de uma determinada sequência de máscaras a partir da posição atual, deixando a possibilidade de o texto ser completamente ignorado ao analisar conjuntos de caracteres arbitrários entre estruturas repetidas detectadas. No entanto, isso não permitiu definir a próxima máscara, independentemente dos resultados da pesquisa para o fragmento anterior pela máscara correspondente: a rigidez da estrutura descrita do texto ainda não deixava espaço para inclusões arbitrárias de caracteres irregulares.

E se, para os exemplos de índice que me ocorreram, esse problema ainda não parecia tão sério, ao tentar aplicar o novo mecanismo de análise a uma tarefa semelhante, em essência, para analisar o conteúdo do site (ou seja, a mesma análise), suas limitações estão aqui. eles apareceram com todas as suas evidências. Afinal, é bastante simples definir as máscaras necessárias para os fragmentos de marcação da Web, entre os quais devem estar os dados que estamos procurando (que você precisa extrair), mas como fazer o analisador ir imediatamente para o próximo fragmento semelhante, apesar de todas as possíveis tags e atributos HTML que podem caber as lacunas entre eles?

Depois de pensar um pouco, decidi introduzir alguns padrões de utilidade (% all_before) e (% all_after) , que servem ao objetivo óbvio de garantir omissões de tudo o que pode estar contido no texto de origem antes de qualquer padrão subsequente (máscara). Além disso, se (% all_before) simplesmente ignorou todas essas inclusões arbitrárias, (% all_after) , pelo contrário, permitiu que elas fossem adicionadas ao fragmento desejado após a mudança do fragmento anterior. Parece bem simples, mas, para implementar esse conceito, tive que "vasculhar" as fontes gorp novamente para fazer as modificações necessárias para não quebrar a lógica já implementada.No final, eu consegui fazer isso (embora até mesmo a primeira, ainda que muito complicada, implementação do meu analisador tenha sido escrita e ainda mais rápida - em algumas semanas). A partir de agora, o sistema assumiu uma forma verdadeiramente universal - não menos de 12 anos após as primeiras tentativas de fazê-lo funcionar.

Claro, este não é o sonho final. Você ainda pode reescrever completamente o analisador de modelos do gorp em C # usando qualquer uma das bibliotecas disponíveis para implementar a gramática livre. Eu acho que o código deve ser bastante simplificado, e isso eliminará o legado na forma de fontes Java existentes. Mas com o mecanismo existente, também é possível fazer várias coisas interessantes, incluindo uma tentativa de implementar os meta-modelos que eu já mencionei, sem mencionar a análise de vários dados de vários sites (no entanto, não excluo que as ferramentas de software especializadas existentes sejam mais adequadas para isso - Eu simplesmente não tive a experiência relevante de usá-los).

A propósito, neste verão eu já recebi um convite por e-mail de uma empresa que usa a tecnologia Salesforce (o desenvolvedor do Gorp original ) para passar uma entrevista para mais trabalhos em Riga. Infelizmente, no momento não estou pronto para essas realocações.

A segunda parte descreve mais detalhadamente a tecnologia para compilar e subsequentemente analisar modelos usando o exemplo de implementação usado no Salesforce Gorp (minhas próprias adições, com exceção de algumas palavras de serviço já descritas, praticamente não altera a sintaxe do modelo, portanto, quase toda a documentação do sistema original GorpApropriado para a minha versão). Lá você encontrará um exemplo de código para interagir com esse mecanismo e um link para o repositório da minha versão - Gorp.NET .

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


All Articles