Историята на един малък проект, дълъг дванадесет години (за BIRMA.NET за първи път и откровено от първо лице)

Раждането на този проект може да се счита за малка идея, която ми хрумна някъде в края на 2007 г., която беше предопределена да намери своя окончателен вид едва 12 години по-късно (в този момент от времето - разбира се, въпреки че текущата реализация, според за автора, е много задоволително) .

Всичко започна, когато в процеса на изпълнение на тогавашните ми служебни задължения в библиотеката обърнах внимание на факта, че процесът на въвеждане на данни от сканирания текст на съдържанието на книжни (и музикални) публикации в съществуващата база данни, очевидно може значително да се опрости и автоматизира, като се възползва от свойството на подреденост и повторяемост на всички данни, необходими за въвеждане, като името на автора на статията (ако говорим за колекция от статии), заглавието на статията (или подзаглавието, отразено в съдържанието) и номера на страницата на текущия елемент от съдържанието. Отначало бях практически убеден, че система, подходяща за изпълнението на тази задача, може лесно да бъде намерена в Интернет. Когато се изненадах от факта, че не можах да намеря такъв проект, реших да се опитам да го реализирам сам.

След доста кратко време започна да работи първият прототип, който веднага започнах да използвам в ежедневните си дейности, като едновременно с това го отстранявах на всички примери, които ми попаднаха. За щастие, на обичайното ми работно място, където в никакъв случай не бях програмист, тогава все пак се измъкнах с видим „престой“ в работата си, по време на който интензивно отстранявах грешки в моето въображение – нещо почти немислимо в сегашните реалности, които предполагат ежедневни отчети за свършената работа през деня. Процесът на полиране на програмата отне общо не по-малко от около година, но дори и след това резултатът трудно би могъл да се нарече напълно успешен - първоначално бяха заложени твърде много различни концепции, които не бяха напълно ясни за изпълнение: незадължителни елементи, които могат да се пропусне; гледане напред на елементи (с цел заместване на предишни елементи в резултатите от търсенето); дори нашия собствен опит да имплементираме нещо като регулярни изрази (което има уникален синтаксис). Трябва да кажа, че преди това бях изоставил донякъде програмирането (за около 8 години, ако не и повече), така че новата възможност да приложа уменията си в една интересна и необходима задача напълно привлече вниманието ми. Не е изненадващо, че полученият изходен код - при липсата на ясни подходи към дизайна му от моя страна - доста бързо се превърна в невъобразима смесица от различни части на езика C с някои елементи на C++ и аспекти на визуалното програмиране (първоначално това беше решено да се използва такава система за проектиране като Borland C++ Builder - „почти Delphi, но в C“). Всичко това обаче в крайна сметка даде резултат в автоматизирането на ежедневната дейност на нашата библиотека.

В същото време реших, за всеки случай, да взема курсове за обучение на професионални разработчици на софтуер. Не знам дали всъщност е възможно да се науча „да бъда програмист“ от нулата там, но като се вземат предвид уменията, които вече имах по това време, успях донякъде да овладея технологии, които бяха по-актуални по това време, като като C#, Visual Studio за разработка под .NET, както и някои технологии, свързани с Java, HTML и SQL. Цялото обучение отне общо две години и послужи като отправна точка за друг мой проект, който в крайна сметка продължи няколко години – но това е тема за отделна публикация. Тук би било уместно само да отбележа, че направих опит да адаптирам разработките, които вече имах по описания проект, за да създам пълноценно прозоречно приложение в C# и WinForms, което реализира необходимата функционалност, и да го използвам като основа за предстоящ дипломен проект.
С течение на времето тази идея започна да ми се струва достойна да бъде изразена на такива годишни конференции с участието на представители на различни библиотеки като „ЛИБКОМ“ и „КРИМ“. Идеята, да, но не и моето изпълнение по това време. Тогава също се надявах, че някой ще го пренапише, използвайки по-компетентни подходи. По един или друг начин, до 2013 г. реших да напиша доклад за предварителната си работа и да го изпратя на Организационния комитет на конференцията с молба за грант за участие в конференцията. За моя известна изненада кандидатурата ми беше одобрена и започнах да правя някои подобрения в проекта, за да го подготвя за представяне на конференцията.

По това време проектът вече беше получил ново име BIRMA, придоби различни допълнителни (не толкова напълно внедрени, а по-скоро предполагаеми) възможности - всички подробности можете да намерите в моя доклад.

Честно казано, трудно можеше да се нарече BIRMA 2013 нещо завършено; Честно казано, това беше много хакерски занаят, направен набързо. По отношение на кода практически нямаше никакви специални нововъведения, с изключение на доста безпомощен опит да се създаде някакъв унифициран синтаксис за анализатора, на външен вид напомнящ езика за форматиране IRBIS 64 (а всъщност и системата ISIS - със скоби като циклични структури; защо тогава мислех, че изглежда доста готино). Анализаторът безнадеждно се натъкна на тези кръгове от скоби от съответния тип (тъй като скобите изпълняваха и друга роля, а именно те маркираха незадължителни структури по време на анализиране, които могат да бъдат пропуснати). Отново препращам всеки, който иска да се запознае по-подробно с тогавашния труден за представяне, неоправдан синтаксис на БИРМА, към тогавашния ми доклад.

Като цяло, освен че се боря със собствения си анализатор, нямам какво повече да кажа относно кода на тази версия - освен обратното преобразуване на съществуващите източници в C++, като същевременно запазва някои типични характеристики на .NET кода (честно казано, това е трудно за разбиране, какво точно ме подтикна да преместя всичко назад - вероятно някакъв глупав страх да запазя изходните си кодове в тайна, сякаш беше нещо еквивалентно на тайната рецепта на Coca-Cola).

Може би в това глупаво решение се крие и причината за трудностите при сдвояването на получената DLL библиотека със съществуващия интерфейс на домашна работна станция за въвеждане на данни в електронен каталог (да, не споменах друг важен факт: отсега нататък всички кодът на „двигателя“ на BIRMA беше както се очакваше, той е отделен от интерфейсната част и пакетиран в съответния DLL). Защо е необходимо да се напише отделна работна станция за тези цели, която така или иначе по външния си вид и метода на взаимодействие с потребителя безсрамно копира същата работна станция „Каталогизатор“ на системата IRBIS 64 - това е отделен въпрос. Накратко: това даде необходимата солидност на моите тогавашни разработки за моя дипломен проект (в противен случай само несмилаемият парсер двигател някак не беше достатъчен). В допълнение, тогава срещнах някои трудности при внедряването на интерфейса на работната станция Cataloger с моите собствени модули, реализирани както на C++, така и на C#, и директния достъп до моя двигател.

Като цяло, колкото и да е странно, именно този доста тромав прототип на бъдещия BIRMA.NET беше предопределен да стане моя „работен кон“ през следващите четири години. Не може да се каже, че през това време поне не се опитах да намеря начини за ново, по-пълно осъществяване на една дългогодишна идея. Сред другите нововъведения вече трябваше да има вложени циклични последователности, които можеха да включват незадължителни елементи - така щях да оживя идеята за ​универсални шаблони за библиографски описания на публикации и различни други интересни неща. Но в моята практическа дейност по това време всичко това беше малко търсено и изпълнението, което имах по това време, беше напълно достатъчно за въвеждане на съдържание. Освен това векторът на развитие на нашата библиотека започна все повече да се отклонява към цифровизацията на музейните архиви, отчетността и други дейности, които не ме интересуваха много, което в крайна сметка ме принуди окончателно да я напусна, давайки път на тези, които биха бъдете по-доволни от всичко това.

Парадоксално, но именно след тези драматични събития проектът БИРМА, който по онова време вече притежава всички характерни черти на типичен дългосрочен проект, сякаш започва да заживява своя дългоочакван нов живот! Имах повече свободно време за празни мисли, отново започнах да претърсвам световната мрежа в търсене на нещо подобно (за щастие вече можех да се досетя да търся всичко това не къде да е, а в GitHub) и някъде в началото на тази година най-накрая попаднах на съответния продукт от известната компания Salesforce под незначителното име Gorp. Сам по себе си той можеше да направи почти всичко, от което имах нужда от такъв парсер двигател - а именно, интелигентно изолиране на отделни фрагменти от произволен, но ясно структуриран текст, като същевременно имаше доста удобен за крайния потребител интерфейс, включително такива разбираеми същности, като модел, шаблон и срещане, като в същото време използва познатия синтаксис на регулярните изрази, който става несравнимо по-четлив поради разделянето на определени семантични групи за анализ.

Като цяло реших, че това е този Gorp (Чудя се какво означава това име? Може би някакъв вид „общо ориентиран регулярен парсер“?) – точно това, което търсех от дълго време. Вярно, непосредственото му прилагане за моите собствени нужди имаше такъв проблем, че този двигател изискваше твърде стриктно придържане към структурната последователност на изходния текст. За някои отчети като регистрационни файлове (а именно те бяха поставени от разработчиците като ясни примери за използване на проекта) това е доста подходящо, но за същите текстове на сканирани таблици със съдържание е малко вероятно. В края на краищата същата страница със съдържание може да започва с думите „Съдържание“, „Съдържание“ и всякакви други предварителни описания, които не е необходимо да поставяме в резултатите от планирания анализ (и да ги отрязваме ръчно всеки път също е неудобен). В допълнение, между отделните повтарящи се елементи, като име на автора, заглавие и номер на страница, страницата може да съдържа известно количество боклук (например рисунки и просто произволни знаци), което също би било хубаво да можете отрязвам. Последният аспект обаче все още не беше толкова значим, но поради първия, съществуващата реализация не можеше да започне да търси необходимите структури в текста от определено място, а вместо това просто го обработваше от самото начало, не намираше определих модели там и... приключих работата си. Очевидно беше необходима известна корекция, за да се остави поне известно пространство между повтарящите се структури, и това ме върна на работа.

Друг проблем беше, че самият проект беше реализиран на Java и ако планирах в бъдеще да внедря някои средства за взаимодействие на тази технология с познати приложения за въвеждане на данни в съществуващи бази данни (като „Каталогизатора“ на Irbis), тогава поне поне направете това в C# и .NET. Не че Java сама по себе си е лош език – веднъж дори го използвах, за да реализирам интересно прозоречно приложение, което реализира функционалността на домашен програмируем калкулатор (като част от курсов проект). И по отношение на синтаксиса е много подобен на същия C-sharp. Е, това е само плюс: толкова по-лесно ще ми бъде да финализирам съществуващ проект. Въпреки това не исках да се потопя отново в този доста необичаен свят на прозоречни (или по-скоро настолни) Java технологии - в края на краищата, самият език не беше „пригоден“ за такава употреба и изобщо не жадувах за повторение на предишния опит. Може би точно защото C# във връзка с WinForms е много по-близо до Delphi, с който много от нас някога са започнали. За щастие необходимото решение беше намерено доста бързо - под формата на проекта IKVM.NET, което улеснява превода на съществуващи Java програми в управляван .NET код. Вярно, самият проект вече беше изоставен от авторите по това време, но последното му изпълнение ми позволи доста успешно да извърша необходимите действия за изходните текстове Gorp.

Така че направих всички необходими промени и сглобих всичко в DLL от подходящия тип, който лесно можеше да бъде „подхванат“ от всякакви проекти за .NET Framework, създадени във Visual Studio. Междувременно създадох друг слой за удобно представяне на върнатите резултати Gorp, под формата на съответстващи структури от данни, които биха били удобни за обработка в табличен изглед (като се вземат за основа както редове, така и колони; както речникови ключове, така и цифрови индекси). Е, самите необходими помощни програми за обработка и показване на резултатите бяха написани доста бързо.

Освен това процесът на адаптиране на шаблони за новия двигател, за да се научи да анализира съществуващи проби от сканирани текстове на таблици със съдържание, не предизвика никакви специални усложнения. Всъщност изобщо не трябваше да се позовавам на предишните си шаблони: просто създадох всички необходими шаблони от нулата. Освен това, ако шаблоните, предназначени да работят с предишната версия на системата, задават доста тясна рамка за текстове, които могат да бъдат правилно анализирани с тяхна помощ, новият двигател вече направи възможно разработването на доста универсални шаблони, подходящи за няколко типа маркиране на веднъж. Дори се опитах да напиша някакъв вид изчерпателен шаблон за произволен текст със съдържание, въпреки че, разбира се, дори и с всички нови възможности, които се отварят за мен, включително, по-специално, ограничената възможност за прилагане на едни и същи вложени повтарящи се последователности ( като например фамилни имена и инициали на няколко автора подред), това се оказа утопия.

Може би в бъдеще ще бъде възможно да се приложи определена концепция за меташаблони, които ще могат да проверяват изходния текст за съответствие с няколко от наличните шаблони наведнъж и след това, в съответствие с получените резултати, да изберете най-подходящият, използващ някакъв вид интелигентен алгоритъм. Но сега бях по-загрижен за друг въпрос. Парсер като Gorp, въпреки цялата му гъвкавост и модификациите, които направих, той все още по своята същност беше неспособен да направи едно привидно просто нещо, което моят самостоятелно написан анализатор можеше да направи от първата версия. А именно: той имаше способността да намира и извлича от изходния текст всички фрагменти, които отговарят на маската, посочена в шаблона, използван на правилното място, без изобщо да се интересува какво съдържа дадения текст в интервалите между тези фрагменти. Досега съм подобрил само леко новия двигател, позволявайки му да търси всички възможни нови повторения на дадена последователност от такива маски от текущата позиция, оставяйки възможността за присъствие в текста на набори от произволни знаци, които са били напълно неотчетени при анализа, затворени между откритите повтарящи се структури. Това обаче не позволи да се зададе следващата маска, независимо от резултатите от търсенето на предишния фрагмент с помощта на съответната маска: строгостта на описаната текстова структура все още не оставя място за произволни включвания на неправилни знаци.

И ако за примерите за таблици със съдържание, на които се натъкнах, този проблем все още не изглеждаше толкова сериозен, тогава когато се опитвате да приложите нов механизъм за анализиране към подобна задача за анализиране на съдържанието на уебсайт (т.е. същия анализ), неговият ограниченията са тук, те се появиха с цялата си очевидност. В края на краищата е доста лесно да зададете необходимите маски за фрагменти от уеб маркиране, между които трябва да се намират данните, които търсим (които трябва да бъдат извлечени), но как можем да принудим анализатора незабавно да премине към следващия подобен фрагмент, въпреки всички възможни тагове и HTML атрибути, които могат да бъдат поставени в интервалите между тях?

След като помислих малко, реших да представя няколко модела на обслужване (%всички_преди) и (%всички_след), обслужващи очевидната цел да гарантират, че всичко, което може да се съдържа в изходния текст, е пропуснато преди всеки модел (маска), който ги следва. Освен това, ако (%всички_преди) тогава просто игнорира всички тези произволни включвания (%всички_след), напротив, им позволи да бъдат добавени към желания фрагмент след преместване от предишния фрагмент. Звучи доста просто, но за да приложа тази концепция, трябваше отново да прегледам gorp източниците, за да направя необходимите модификации, за да не наруша вече внедрената логика. В крайна сметка успяхме да направим това (въпреки че дори най-първото, макар и много бъгово, изпълнение на моя анализатор беше написано и дори по-бързо - за няколко седмици). Оттук нататък системата придобива наистина универсална форма - не по-малко от 12 години след първите опити да функционира.

Разбира се, това не е краят на нашите мечти. Можете също така напълно да пренапишете парсера на горп шаблона в C#, като използвате някоя от наличните библиотеки за внедряване на безплатна граматика. Мисля, че кодът трябва да бъде значително опростен и това ще ни позволи да се отървем от наследството под формата на съществуващи източници на Java. Но със съществуващия тип двигател също е напълно възможно да се правят различни интересни неща, включително опит за внедряване на мета-шаблоните, които вече споменах, да не говорим за анализиране на различни данни от различни уебсайтове (обаче не изключвам че съществуващите специализирани софтуерни инструменти са по-подходящи за това – просто все още не съм имал подходящия опит с използването им).

Между другото, това лято вече получих покана по имейл от компания, която използва технологиите на Salesforce (разработчикът на оригиналния Gorp), преминете интервю за последваща работа в Рига. За съжаление в момента не съм готов за подобни преразпределения.

Ако този материал предизвика известен интерес, тогава във втората част ще се опитам да опиша по-подробно технологията за компилиране и последващо анализиране на шаблони, като използвам примера на внедряването, използвано в Salesforce Gorp (моите собствени допълнения, с изключение на няколко вече описани функционални думи, не правят практически никакви промени в самия синтаксис на шаблона, така че почти цялата документация за оригиналната система Gorp Подходящо и за моята версия).

Източник: www.habr.com

Добавяне на нов коментар