Гісторыя аднаго невялікага праекта даўжынёю ў дванаццаць гадоў (пра BIRMA.NET упершыню і шчыра з першых рук)

Нараджэннем гэтага праекта можна лічыць маленькую ідэю, якая наведала мяне дзесьці ў канцы 2007 года, якой наканавана было набыць свой канчатковы выгляд толькі праз 12 гадоў (на дадзены момант часу - вядома ж, хоць і бягучая рэалізацыя, на думку аўтара, вельмі здавальняючая) .

Усё пачалося з таго, што ў працэсе выканання сваіх тагачасных службовых абавязкаў у бібліятэцы я звярнуў увагу на тую акалічнасць, што працэс уводу дадзеных з адсканаванага тэксту зместаў кніжных (і нотных) выданняў у наяўную базу дадзеных, відаць, можна ў значнай ступені спрасціць і аўтаматызаваць, карыстаючыся ўласцівасцю спарадкаванасці і паўтаранасці ўсіх дадзеных, неабходных для ўводу, такіх як імя аўтара артыкула (калі мы гаворым аб зборніку артыкулаў), назву артыкула (ці адлюстраваны ў зместе падзагаловак) і нумар старонкі бягучага пункта зместа. На першую пару я быў практычна перакананы ў тым, што сістэму, прыдатную для ажыццяўлення дадзенай задачы, можна лёгка знайсці на прасторах Інтэрнэту. Калі ж некаторае здзіўленне выклікаў той факт, што падобны праект мне адшукаць так і не ўдалося, я прыняў рашэнне паспрабаваць рэалізаваць яго ўласнымі сіламі.

Праз досыць непрацяглы час зарабіў першы прататып, які я адразу ж пачаў выкарыстоўваў у сваёй паўсядзённай дзейнасці, адначасна адладжваючы яго на ўсіх якія трапляюцца мне пад руку прыкладах. На шчасце, на маім звыклым працоўным месцы, дзе я лічыўся зусім не праграмістам, мне тады яшчэ сыходзілі з рук бачныя «прастоі» у працы, на працягу якіх я ўзмоцнена займаўся адладкай свайго стварэння – практычна неймаверная справа ў цяперашніх рэаліях, якія разумеюць штодзённыя справаздачы аб праведзенай за дзень рабоце. Працэс шліфоўкі праграмы заняў у агульнай складанасці ні многа ні мала каля года, але нават і пасля гэтага вынік складана было назваць цалкам паспяховым – занадта ўжо шмат там першапачаткова было закладзена розных не зусім зразумелых для рэалізацыі канцэпцый: неабавязковыя элементы, якія можна прапускаць; апераджальны прагляд элементаў (для мэт падстаноўкі да вынікаў пошуку папярэдніх элементаў); нават уласная спроба рэалізацыі чагосьці накшталт рэгулярных выразаў (які мае самабытны сінтаксіс). Трэба сказаць, што да гэтага я паспеў некалькі падзакінуць праграмаванне (гадоў так на 8, калі не больш), так што новая магчымасць прыкласці свае навыкі да цікавай і патрэбнай задачы цалкам авалодала маёй увагай. Нядзіўна, што атрыманы ў выніку зыходны код - у адсутнасць з майго боку якіх-небудзь выразных падыходаў да яго праектавання - даволі хутка стаў уяўляць сабой няўяўную мешаніну разрозненых кавалкаў на мове Сі з некаторымі элементамі Сі ++ і аспектамі візуальнага праграмавання (першапачаткова было вырашана выкарыстоўваць такую ​​сістэму праектавання, як Borland C++ Builder – "амаль Delphi, але на Сі"). Аднак усё гэта ў выніку прынесла свой плён у справе аўтаматызацыі паўсядзённай дзейнасці нашай бібліятэкі.

Адначасова з гэтым я вырашыў на ўсялякі выпадак прайсці курсы па падрыхтоўцы прафесійных распрацоўшчыкаў ПЗ. Не ведаю, ці можна там насамрэч з нуля вывучыцца «на праграміста», але з улікам ужо наяўных у мяне на той час навыкаў мне ўдалося крыху асвоіць такія ўжо больш актуальныя да таго моманту тэхналогіі, як C#, Visual Studio для распрацоўкі пад . NET, а таксама крыху з тэхналогій, якія адносяцца да Java, HTML і SQL. Усё навучанне заняло ў агульнай складанасці два гады, і паслужыла зыходным пунктам для яшчэ аднаго майго праекта, які ў рэшце рэшт расцягнуўся на некалькі гадоў - але гэта ўжо тэма для асобнай публікацыі. Тут жа будзе дарэчы адзначыць толькі тое, што я зрабіў спробу адаптаваць ужо наяўныя ў мяне напрацоўкі па апісваным праекце для стварэння паўнавартаснага аконнага прыкладання на C# і WinForms, які рэалізуе патрэбны функцыянал, і пакласці яго ў аснову маючага адбыцца дыпломнага праекта.
З часам дадзеная ідэя пачала здавацца мне дастойнай быць агучанай і на такіх штогадовых канферэнцыях з удзелам прадстаўнікоў розных бібліятэк, як “ЛІБКОМ” і “КРЫМ”. Ідэя - так, але зусім не мая тагачасная яе рэалізацыя. Тады яшчэ я спадзяваўся ў тым ліку і на тое, што хто-небудзь перапіша яе з ужываннем граматнейшых падыходаў. Так ці інакш, да 2013 года я вырашыўся скласці даклад аб сваёй папярэдняй працы і даслаць яго ў Аргкамітэт канферэнцыі з заяўкай на атрыманне гранта для ўдзелу ў канферэнцыі. Да майго некаторага здзіўлення, мая заяўка была задаволена, і я пачаў уносіць некаторыя дапрацоўкі ў праект для яго падрыхтоўкі да прадстаўлення на канферэнцыі.

Да таго часу праект ужо атрымаў новае імя BIRMA, набыў розныя дадатковыя (не столькі паўнавартасна рэалізаваныя, колькі меркаваныя) магчымасці - усе падрабязнасці можна знайсці ў маім дакладзе.

Прызнацца шчыра, BIRMA узору 2013 года складана было назваць нечым скончаным; шчыра кажучы, яна ўяўляла сабой выкананую на хуткую руку вельмі халтурны выраб. Па кодавай частцы наогул практычна не было ніякіх адмысловых новаўвядзенняў, не лічачы даволі бездапаможную спробу стварэння некаго ўніфікаванага сінтаксісу для парсера, па сваім вонкавым выглядзе які нагадвае мову фарматавання ИРБИС 64 (а ў сутнасці, яшчэ сістэмы ISIS – з круглымі дужкамі ў ролі цыклічных структур; -то тады мне здавалася, што гэта выглядае вельмі крута). Сінтаксічны аналізатар безнадзейна спатыкаўся на гэтых кругазваротах з дужак адпаведнага віду (паколькі круглыя ​​дужкі выконвалі там жа і яшчэ адну ролю, а менавіта - імі адзначаліся неабавязковыя структуры падчас разбору, якія можна прапусціць). Усіх жадаючых азнаёміцца ​​падрабязней з тагачасным цяжка ўяўным, нічым не апраўданым сінтаксісам BIRMA, я ізноў жа адсылаю да свайго дакладу таго часу.

Увогуле, калі не лічыць дужання з уласным сінтаксічным аналізатарам, то па частцы кода гэтай версіі мне больш сказаць і няма чаго – калі не лічыць зваротнай канвертацыі наяўных зыходнікаў на Сі++ з захаваннем некаторых тыповых рыс .NET-кода (сумленна кажучы, цяжка зразумець , што менавіта заахвоціла мяне перанесці ўсё зваротна – мусіць, нейкая дурная асцярога за захаванасць у таямніцы сваіх зыходных кодаў, нібы гэта было нешта раўнацэннае сакрэтнаму рэцэпту Coca-Cola).

Магчыма, у гэтым дурным рашэнні крыецца таксама і чыннік складанасцяў у спалучэнні атрыманай DLL-бібліятэкі з які быў інтэрфейсам самапальнага АРМа для ўводу дадзеных у электронны каталог (так, я ж не згадаў яшчэ пра адзін немалаважны факт: з гэтага часу ўвесь код «рухавічка» BIRMA быў, як і належыць, аддзелены ад інтэрфейснай часткі і спакаваны ў адпаведную DLL). Навошта спатрэбілася пісаць яшчэ і асобны АРМ для гэтых мэт, які ўсё роўна па сваім вонкавым выглядзе і спосабу ўзаемадзеяння з карыстачом беспардонна капіяваў той жа АРМ «Каталогізатар» сістэмы ИРБИС 64 – гэта асобнае пытанне. Калі сцісла: ён надаваў належную самавітасць маім тагачасным напрацоўкам для дыпломнага праекту (а то аднаго толькі нязручнага рухавічка парсера было неяк замала). Акрамя таго, я тады сутыкнуўся з некаторымі цяжкасцямі пры рэалізацыі спалучэння АРМ «Каталогізатар» з уласнымі модулямі, рэалізаванымі што на C++, што на C#, і якія звяртаюцца ўжо непасрэдна да майго рухавічка.

Увогуле, як гэта ні дзіўна, але менавіта гэтаму даволі нязграбнаму прататыпу будучай BIRMA.NET і наканавана было стаць маім «працоўным конікам» яшчэ на наступныя чатыры гады. Нельга сказаць, што на працягу гэтага часу я не спрабаваў хаця б намацаць шляхі для новай, ужо больш паўнавартаснай рэалізацыі даўняй задумкі. Тамака павінны былі ўжо прысутнічаць у ліку іншых новаўвядзенняў і ўкладзеныя цыклічныя паслядоўнасці, якія маглі б складацца з у тым ліку і неабавязковыя элементы – менавіта так я збіраўся ўвасобіць у жыццё ідэю ўніверсальных шаблонаў для бібліяграфічнага апісання выданняў і розных іншых цікавых рэчаў. Аднак у маёй тагачаснай практычнай дзейнасці ўсё гэта было слаба запатрабавана, а для ўводу зместаў хапала і наяўнай у мяне на той момант рэалізацыі. Акрамя таго, вектар напрамку развіцця нашай бібліятэкі пачаў усё больш ухіляцца ў бок аблічбоўкі музейных архіваў, фарміравання справаздачнасці і іншых малацікавых для мяне заняткаў, што ў рэшце рэшт і прымусіла мяне канчаткова яе пакінуць, саступіўшы сваё месца тым, каму ўсё гэта было б больш прыемна. .

Як ні парадаксальна, але менавіта пасля гэтых драматычных падзей праект BIRMA, на той момант ужо які валодаў усімі характэрнымі рысамі тыповага даўгабуда, здавалася, пачаў здабываць сваё доўгачаканае новае жыццё! У мяне з'явілася больш вольнага часу на пустыя разважанні, я зноў пачаў прачэсваць Сусветную сетку ў пошуках чагосьці падобнага (балазе, зараз я ўжо мог здагадацца шукаць усё гэта не дзе патрапіла, а менавіта на GitHub'е), і дзесьці ў пачатку гэтага года я нарэшце натыкнуўся на адпаведны выраб даволі вядомай канторы Salesforce пад малазначнай назвай Gorp. Яна ўжо сама па сабе магла рабіць практычна ўсё, што мне было трэба ад такога парсернага рухавічка – а менавіта, па-разумнаму вычляняе асобныя фрагменты з адвольнага, але які валодае выразнай структурай тэксту, пры гэтым валодаючы даволі усялякім інтэрфейсам для канчатковага карыстача, улучальным такія зразумелыя сутнасці, як патэрн, шаблон і ўваходжанне, і якія задзейнічаюць у той жа час звыклы сінтаксіс рэгулярных выразаў, які робіцца непараўнальна больш чытэльным у сілу разбіцця на азначаныя сэнсавыя групы для разбору.

Увогуле, я вырашыў, што гэты самы Gorp (цікава, што гэта назва азначае? Быць можа, які-небудзь "general oriented regular parser"?) - менавіта тое, што я даўно і шукаў. Праўда, яго неадкладнае ўкараненне для маіх уласных патрэб мела такую ​​праблему, што дадзены рухавік патрабаваў занадта няўхільна дакладнага захавання структурнай паслядоўнасці зыходнага тэксту. Для якіх-небудзь справаздач тыпу log-файлаў (а менавіта яны і былі змешчаныя распрацоўнікамі ў якасці наглядных прыкладаў выкарыстання праекта) гэта цалкам спатрэбіцца, а вось для тых жа тэкстаў адсканаваных зместаў – ці наўрад. Бо тая ж самая старонка з зместам можа пачынацца са слова "Змест", "Змест" і яшчэ якіх-небудзь папярэдніх апісанняў, якія нам зусім не трэба змяшчаць у вынікі меркаванага разбору (а адсякаць іх кожны раз уручную таксама няёмка). Акрамя таго, паміж асобнымі паўтаральнымі элементамі, такімі як імя аўтара, назва і нумар старонкі, на старонцы можа змяшчацца некаторая колькасць смецця (напрыклад, малюнкі, ды і проста выпадковыя знакі), якое таксама нядрэнна б умець адсякаць. Зрэшты, апошні аспект быў яшчэ не такі значны, а вось з-за першага наяўная рэалізацыя не магла пачаць шукаць патрэбныя структуры ў тэксце з нейкага пэўнага месца, а замест гэтага проста апрацоўвала яго з самага пачатку, не знаходзіла там названых шаблонаў і... канчала сваю працу. Відавочна, што патрабавалася адпаведная дапрацоўка, якая дазволіла б прынамсі пакідаць некаторыя прамежкі паміж паўтаральнымі структурамі, і гэта прымусіла мяне зноў засесці за працу.

Іншая праблема складалася ў тым, што сам праект быў рэалізаваны на Java, а я, калі і планаваў у далейшым рэалізоўваць некаторыя сродкі спалучэння дадзенай тэхналогіі са звыклымі прыкладаннямі для ўводу дадзеных у наяўныя базы (такімі як ірбісаўскі «Каталогізатар»), то ўжо па меншай меры рабіць гэта на C # і. NET. Не тое, каб Java сама па сабе была дрэнная мова – калісьці я нават менавіта на ім рэалізаваў цікавы аконны дадатак, якое рэалізуе функцыянал айчыннага праграмуемага калькулятара (у рамках курсавога праекта). Ды і па сінтаксісе ён вельмі падобны з ​​тым жа Сі-шарпа. Што ж, гэта толькі плюс: тым лягчэй мне будзе дапрацаваць ужо існуючы праект. Аднак мне не жадалася ізноў апускацца ў гэты даволі нязвыклы свет аконных (дакладней, дэсктопных) Java-тэхналогій у выніку, сама мова не быў "заменчаны" на такое выкарыстанне, а я зусім не прагнуў паўтарэння ранейшага досведу. Магчыма, менавіта з-за таго, што C# у звязку з WinForms нашмат бліжэй да Delphi, з якога шматлікія з нас калісьці пачыналі. На шчасце, патрэбнае рашэнне знайшлося даволі хутка - у асобе праекта IKVM.NET, які дазваляе лёгка ператрансляваць наяўныя праграмы на Java у кіраваны код. NET. Праўда, сам праект да таго моманту ўжо быў закінуты аўтарамі, аднак яго апошняя рэалізацыя дазволіла мне паспяхова прарабіць патрэбныя дзеянні для зыходных тэкстаў. Gorp.

Так што я занёс усе неабходныя праўкі і скампанаваў гэта ўсё ў DLL які адпавядае выгляду, якую ўжо лёгка маглі «падхапіць» любыя праекты для .NET Framework, ствараныя ў Visual Studio. Паміж справай я стварыў яшчэ адну праслойку для зручнага ўяўлення вынікаў, якія вяртаюцца Gorp, у выглядзе адпаведных структур дадзеных, якія было б зручна апрацоўваць у таблічным прадстаўленні (прычым беручы за аснову як радкі, так і слупкі; як слоўнікавыя ключы, так і лікавыя індэксы). Ну, а самі патрэбныя ўтыліты для апрацоўкі і адлюстраванні атрыманых вынікаў напісаліся ўжо даволі хутка.

Гэтак жа адмысловых ускладненняў не выклікаў і працэс адаптацыі шаблонаў для новага рухавічка з мэтай навучыць яго разбіраць наяўныя ўзоры адсканаваных тэкстаў зместаў. Па сутнасці, мне нават наогул не прыйшлося звяртацца да сваіх ранейшых нарыхтовак: я проста стварыў усе неабходныя шаблоны з нуля. Больш за тое, калі шаблоны, прызначаныя для працы з ранейшай версіяй сістэмы, задавалі досыць вузкія рамкі для тэкстаў, якія паддаюцца правільнаму разбору з іх дапамогай, то новы рухавік ужо дазваляў распрацоўваць досыць універсальныя шаблоны, прыдатныя для некалькіх тыпаў разметкі адразу. Я нават паспрабаваў напісаць нейкі ўсёабдымны шаблон для любога адвольнага тэксту зместа, хоць, вядома, нават пры ўсіх адкрываных для мяне новых магчымасцях, улучальных, у прыватнасці, абмежаваную магчымасць рэалізацыі ўсё тых жа ўкладзеных паўтаральных паслядоўнасцяў (такіх, як, напрыклад, прозвішчы і ініцыялы. некалькіх аўтараў, якія ідуць запар), гэта аказалася утопіяй.

Магчыма, у далейшым і можна будзе рэалізаваць нейкую канцэпцыю меташаблонаў, якія змогуць правяраць зыходны тэкст на адпаведнасць адразу некалькім з наяўных шаблонаў, а затым у адпаведнасці з атрыманымі вынікамі выбіраць з іх найболей падыходны, карыстаючыся якім-небудзь інтэлектуальным алгарытмам. Але мяне зараз больш хвалявала іншае пытанне. Такі парсер, як Gorp, нягледзячы на ​​ўсю сваю ўніверсальнасць і ўнесеныя мною мадыфікацыі, па-ранейшаму быў па прыродзе сваёй не здольны выконваць адну, здавалася б, простую рэч, якую мой уласнаручна напісаны парсер умеў рабіць з самай першай версіі. А менавіта: ён валодаў здольнасцю знаходзіць і здабываць з зыходнага тэксту ўсе фрагменты, якія супадаюць з паказанай у рамках выкарыстоўванага шаблону ў патрэбным месцы маскай, пры гэтым зусім не цікавячыся тым, што дадзены тэкст утрымоўвае ў прамежках паміж гэтымі фрагментамі. Да гэтага часу я толькі трохі ўдасканаліў новы рухавічок, дазваляючы яму шукаць усе магчымыя новыя паўторы зададзенай паслядоўнасці такіх масак з бягучай пазіцыі, пакідаючы магчымасць для наяўнасці ў тэксце зусім не ўлічаных пры разборы набораў адвольных знакаў, зняволеных паміж выяўленымі паўтаральнымі структурамі. Аднак гэта не давала магчымасці задаваць чарговую маску незалежна ад вынікаў пошуку папярэдняга фрагмента па адпаведнай яму масцы: строгасць апісванай структуры тэксту па-ранейшаму не пакідала месца для адвольных уключэнняў нерэгулярных сімвалаў.

І калі для якія трапляліся мне прыкладаў зместаў гэтая праблема яшчэ не здавалася такой ужо сур'ёзнай, то вось пры спробе ўжывання новага механізму разбору да падобнай па сваёй сутнасці задачы па разборы змесціва вэб-сайта (г.зн. таму ж самаму парсінгу), яго абмежаванні тут ж паўсталі з усёй сваёй відавочнасцю. Бо даволі проста задаць патрэбныя маскі для фрагментаў вэб-разметкі, паміж якімі павінны размяшчацца шуканыя намі дадзеныя (якія неабходна атрымаць), але як прымусіць парсер адразу ж пераходзіць да наступнага падобнага фрагмента, нягледзячы на ​​ўсе магчымыя тэгі і атрыбуты HTML, якія могуць змяшчацца ў прамежках паміж імі?

Трохі падумаўшы, я вырашыў увесці пару службовых патэрнаў (%all_before) и (%all_after), служачых відавочнай мэты забеспячэння пропускаў усяго таго, што можа змяшчацца ў зыходным тэксце да любога наступнага за імі патэрна (маскі). Пры гэтым калі (%all_before) проста ігнараваў усе гэтыя адвольныя ўключэнні, то (%all_after), наадварот, дазваляў дадаць іх да шуканага фрагмента пасля пераходу ад папярэдняга фрагмента. Гучыць даволі проста, але вось для рэалізацыі дадзенай канцэпцыі мне прыйшлося зноў "прачасаць" gorp'аўскія зыходнікі для занясення патрэбных мадыфікацый з тым, каб пры гэтым не паламаць ужо рэалізаваную логіку. У выніку гэта атрымалася зрабіць (хоць нават самая-самая першая, хай і вельмі глючная рэалізацыя майго парсера была напісана і тое хутчэй - за пару тыдняў). З гэтага часу сістэма прыняла па-сапраўднаму ўніверсальны выгляд праз ні шмат ні мала 12 гадоў пасля першых спроб прымусіць яе функцыянаваць.

Зразумела, гэта яшчэ не мяжа летуценняў. Можна яшчэ цалкам перапісаць сінтаксічны аналізатар gorp'аўскіх шаблонаў на C#, выкарыстоўваючы якую-небудзь з наяўных бібліятэк для рэалізацыі вольнай граматыкі. Я думаю, код пры гэтым павінен значна спрасціцца, і гэта дазволіць пазбавіцца ад спадчыны ў выглядзе наяўных зыходнікаў на Java. Але і з наяўным выглядам рухавічка таксама ўжо цалкам можна рабіць розныя цікавыя рэчы, уключаючы спробу рэалізацыі ўжо згаданых мной меташаблонаў, не кажучы аб парсінгу розных дадзеных з разнастайных вэб-сайтаў (зрэшты, не выключаю, што ўжо наяўныя спецыялізаваныя праграмныя сродкі падыходзяць для гэтага больш – проста ў мяне яшчэ не было адпаведнага досведу іх выкарыстання).

Дарэчы, гэтым летам я ўжо атрымліваў па сваёй электроннай пошце запрашэнне ад кампаніі, якая прымяняе тэхналогіі Salesforce (распрацоўніка арыгінальнага. Gorp), прайсці сумоўе – для наступнай працы ў Рызе. На жаль, у дадзены момант я не гатовы да падобных перадыслакацый.

Калі дадзены матэрыял будзе выклікаць некаторую цікавасць, то ў другой частцы я пастараюся больш падрабязна апісаць тэхналогію складання і наступнага разбору шаблонаў на прыкладзе рэалізацыі, выкарыстоўванай у Salesforce. Gorp (мае ўласныя даданні, за выключэннем пары ўжо апісаных службовых слоў, практычна не ўносяць змен у сам сінтаксіс шаблонаў, так што практычна ўся дакументацыя для арыгінальнай сістэмы Gorp падыходзіць і для маёй версіі).

Крыніца: habr.com

Дадаць каментар