Lugu ühest kaheteistkümne aasta pikkusest väikesest projektist (BIRMA.NETi kohta esimest korda ja ausalt öeldes otsekohene)

Selle projekti sündi võib pidada väikeseks ideeks, mis mul tekkis kuskil 2007. aasta lõpus ja mis pidi oma lõpliku vormi leidma alles 12 aastat hiljem (sel hetkel – muidugi, kuigi praegune teostus, autorile, on väga rahuldav).

Kõik sai alguse sellest, et raamatukogus oma toonaste ametiülesannete täitmise käigus juhtisin tähelepanu asjaolule, et raamatu- (ja noodi-) väljaannete sisukordade skaneeritud tekstist andmete sisestamise protsess olemasolevasse andmebaasi, mille sisuks on XNUMX. aasta aruanne, mis on XNUMX. aasta algusest peale. ilmselt saab oluliselt lihtsustada ja automatiseerida, kasutades ära kõigi sisestamiseks vajalike andmete korrastatuse ja korratavuse omadust, nagu näiteks artikli autori nimi (kui me räägime artiklite kogumikust), artikli pealkiri. artikkel (või sisukorras kajastuv alapealkiri) ja kehtiva sisukorra punkti leheküljenumber. Algul olin praktiliselt veendunud, et selle ülesande täitmiseks sobiva süsteemi on Internetist lihtne leida. Kui üllatuse tekitas asjaolu, et ma sellist projekti ei leidnud, otsustasin proovida seda omal käel ellu viia.

Üsna lühikese aja pärast hakkas tööle esimene prototüüp, mida hakkasin kohe kasutama oma igapäevastes tegevustes, siludes seda samal ajal kõikidel näidetel, mis mulle kätte sattusid. Õnneks pääsesin oma tavapärasel töökohal, kus ma polnud sugugi programmeerija, siiski oma töös nähtavatest „seisakutest“, mille jooksul tegelesin intensiivselt oma vaimusünnitusega silumisega – praeguses reaalsuses peaaegu mõeldamatu asi, mis tähendab päevaaruanded päeva jooksul tehtud tööde kohta. Programmi lihvimisprotsess kestis kokku mitte vähem kui aasta, kuid isegi pärast seda ei saa tulemust täiesti õnnestunuks nimetada - alguses pandi paika liiga palju erinevaid kontseptsioone, mis polnud rakendamiseks täiesti selged: valikulised elemendid, mida saab vahele jätta; elementide edasivaatamine (eesmärgiga asendada varasemad elemendid otsingutulemustes); isegi meie enda katse rakendada midagi regulaaravaldiste sarnast (millel on ainulaadne süntaks). Pean ütlema, et enne seda olin programmeerimisest mõnevõrra loobunud (umbes 8 aastat, kui mitte rohkem), nii et uus võimalus oma oskusi huvitava ja vajaliku ülesande täitmisel rakendada tõmbas mu tähelepanu täielikult. Pole üllatav, et tulemuseks olev lähtekood – ilma minupoolsete selgete lähenemisviiside puudumisel selle kujundusele – muutus üsna kiiresti C-keele erinevate osade kujuteldamatuks segaduseks koos mõne C++ elemendi ja visuaalse programmeerimise aspektidega (alguses see otsustati kasutada sellist disainisüsteemi nagu Borland C++ Builder - "peaaegu Delphi, kuid C"). See kõik aga kandis lõpuks vilja meie raamatukogu igapäevaste tegevuste automatiseerimisel.

Samal ajal otsustasin igaks juhuks läbida professionaalsete tarkvaraarendajate koolitamise kursused. Ma ei tea, kas seal on päriselt võimalik “programmeerijaks” õppida nullist, aga võttes arvesse juba tollal omanud oskusi, suutsin mõneti omandada selleks ajaks aktuaalsemaid tehnoloogiaid, nagu C#, Visual Studio arendamiseks .NET-i all, samuti mõned Java, HTML-i ja SQL-iga seotud tehnoloogiad. Kogu koolitus kestis kokku kaks aastat ja oli lähtepunktiks minu teisele projektile, mis lõpuks venis mitmeks aastaks – aga see on eraldi väljaande teema. Siinkohal oleks paslik vaid märkida, et püüdsin kirjeldatud projekti juures juba tehtud arendusi kohandada, et luua C# ja WinFormsis täisväärtuslik aknarakendus, mis realiseerib vajalikke funktsioone ning võtta selle aluseks eelseisev diplomitöö.
Aja jooksul hakkas see idee mulle tunduma vääriline, et seda sellistel iga-aastastel konverentsidel, kus osalesid erinevate raamatukogude esindajad nagu “LIBKOM” ja “CRIMEA”, välja käia. Idee jah, aga mitte minu toonane teostus. Siis lootsin ka, et keegi kirjutab selle ümber, kasutades pädevamaid lähenemisi. Nii või teisiti otsustasin 2013. aastaks kirjutada oma eeltööst raporti ja saata selle koos taotlusega konverentsil osalemiseks konverentsi korralduskomiteele. Minu mõningaseks üllatuseks kiideti minu taotlus heaks ja ma hakkasin projekti mõningaid parandusi tegema, et valmistada see ette konverentsil esitamiseks.

Selleks ajaks oli projekt saanud juba uue nime BIRMA, omandanud mitmesuguseid täiendavaid (mitte niivõrd täielikult rakendatud, vaid pigem eeldatud) võimeid - kõik üksikasjad leiate minu aruandest.

Kui aus olla, siis BIRMA 2013 oli raske nimetada millekski terviklikuks; Ausalt öeldes oli see väga rumal tehtud käsitöö. Koodi osas ei olnud praktiliselt üldse mingeid erilisi uuendusi, kui välja arvata üsna abitu katse luua parserile mingisugune ühtne süntaks, mis meenutaks välimuselt IRBIS 64 vormindamiskeelt (ja tegelikult ka ISIS süsteemi - sulgudega tsükliliste struktuuridena; miks Sel ajal arvasin, et see nägi päris lahe välja). Parser komistas lootusetult nendele sobivat tüüpi sulgude ringidele (kuna sulud täitsid ka teist rolli, nimelt märkisid nad parsimise käigus valikulisi struktuure, mida saab vahele jätta). Kõik, kes soovivad BIRMA tollal raskesti kujutletava, põhjendamatu süntaksiga lähemalt tutvuda, viitan taas oma toonasele aruandele.

Üldiselt pole mul selle versiooni koodi kohta midagi muud öelda peale selle, et ma olen oma parseriga hädas – välja arvatud olemasolevate allikate tagurpidi konverteerimine C++-vormingusse, säilitades samas mõned .NET-koodi tüüpilised omadused (ausalt öeldes on see raske mõista , mis ajendas mind täpselt kõike tagasi viima - ilmselt mingi loll hirm oma lähtekoodide salajas hoidmise pärast, nagu oleks see midagi samaväärset Coca-Cola salaretseptiga).

Võib-olla peitub selles rumal otsuses ka raskused tekkinud DLL-teegi sidumisel koduse tööjaama olemasoleva liidesega andmete elektroonilisse kataloogi sisestamiseks (jah, ma ei maininud teist olulist fakti: edaspidi on kõik BIRMA “mootori” kood oli ootuspärane, see on liidese osast eraldatud ja pakitud vastavasse DLL-i). Miks oli vaja nendel eesmärkidel kirjutada eraldi tööjaam, mis igatahes oma välimuse ja kasutajaga suhtlemise viisi järgi häbematult kopeeris sama IRBIS 64 süsteemi tööjaama “Katalogiseerija” - see on omaette küsimus. Lühidalt: see andis mu lõputööks vajalikku soliidsust minu toonastele arendustele (muidu seedimatust parser mootorist üksi kuidagi ei piisanud). Lisaks tekkis mul siis mõningaid raskusi Catalogeri tööjaama liidese juurutamisel enda moodulitega, mis on realiseeritud nii C++ kui ka C# keeles, ja otse oma mootorile ligi pääsedes.

Üldiselt, kummalisel kombel, oli see tulevase BIRMA.NETi üsna kohmakas prototüüp, mis oli määratud järgmiseks neljaks aastaks minu "tööhobuseks". Ei saa öelda, et selle aja jooksul poleks ma vähemalt püüdnud leida võimalusi kauaaegse idee uueks, terviklikumaks elluviimiseks. Muude uuenduste hulgas oleks pidanud juba olema pesastatud tsüklilised jadad, mis oleksid võinud sisaldada valikulisi elemente – nii kavatsesin ellu äratada idee universaalsetest mallidest väljaannete bibliograafiliste kirjelduste ja mitme muu huvitava jaoks. Tolleaegses praktilises tegevuses oli see kõik aga vähe nõutud ja toonane teostus oli sisukordade sisestamiseks täiesti piisav. Lisaks hakkas meie raamatukogu arenguvektor üha enam kalduma muuseumiarhiivide digitaliseerimise, aruandluse ja muude mind vähe huvitavate tegevuste suunas, mis lõpuks sundis mind sealt lõpuks lahkuma, andes teed neile, kes seda sooviksid. ole selle kõige üle rohkem rahul.

Paradoksaalsel kombel hakkas just pärast neid dramaatilisi sündmusi BIRMA projekt, millel olid sel ajal juba kõik tüüpilisele pikaajalisele ehitusprojektile iseloomulikud jooned, saama oma kauaoodatud uut elu! Mul oli tühimõtete jaoks rohkem vaba aega, hakkasin jälle World Wide Webi kammima, et midagi sarnast otsida (õnneks oskasin nüüd juba arvata, et otsin seda kõike mitte ainult kuskilt, vaid GitHubist) ja kuskil At the selle aasta alguses sattusin lõpuks ebaolulise nime all tuntud Salesforce firma vastava tooteni Gorp. Iseenesest suudaks see teha peaaegu kõike, mida sellisest parsermootorist vajasin – nimelt eraldada arukalt üksikud fragmendid suvalisest, kuid selgelt struktureeritud tekstist, omades samas lõppkasutaja jaoks üsna kasutajasõbralikku liidest, sealhulgas selliseid arusaadavaid olemusi, nagu muster, mall ja esinemine ning samal ajal tavaavaldiste tuttava süntaksi kasutamine, mis muutub võrreldamatult loetavamaks tänu parsimiseks määratud semantilisteks rühmadeks jagamisele.

Üldiselt otsustasin, et see on see Gorp (Huvitav, mida see nimi tähendab? Võib-olla mingi "üldorienteeritud tavaparser"?) – täpselt see, mida olen kaua otsinud. Tõsi, selle kohese rakendamisega minu enda vajadusteks oli selline probleem, et see mootor nõudis liiga ranget järgimist lähteteksti struktuursest järjestusest. Mõne aruande, näiteks logifailide jaoks (nimelt arendajad panid need projekti kasutamise selgete näidetena) on see üsna sobiv, kuid samade skannitud sisukordade tekstide jaoks on see ebatõenäoline. Lõppude lõpuks võib sama sisukorraga leht alata sõnadega "Sisukord", "Sisukord" ja mis tahes muude esialgsete kirjeldustega, mida meil pole vaja kavandatud analüüsi tulemustes paigutada (ja need käsitsi ära lõigata). iga kord on ka ebamugav). Lisaks võib leht üksikute korduvate elementide, nagu autori nimi, pealkiri ja leheküljenumber, vahel sisaldada teatud kogust prügi (näiteks jooniseid ja lihtsalt juhuslikke tähemärke), mida oleks samuti tore saada. ära lõigatud. Viimane aspekt ei olnud aga veel nii märkimisväärne, kuid esimese tõttu ei saanud olemasolev teostus tekstist teatud kohast vajalikke struktuure otsima hakata, vaid hoopis lihtsalt töötles seda algusest peale, ei leidnud täpsustatud mustrid seal ja... lõpetasin mu töö. Ilmselgelt oli vaja natuke näpistada, et vähemalt korduvate struktuuride vahele ruumi jätta, ja see pani mind tööle tagasi.

Probleemiks oli ka see, et projekt ise viidi ellu Java-s ja kui mul oleks tulevikus plaanis rakendada mingeid vahendeid selle tehnoloogia liidestamiseks tuttavate rakendustega olemasolevatesse andmebaasidesse andmete sisestamiseks (näiteks Irbise “Cataloguer”), siis vähemalt tehke seda C#-s ja .NET-is. Asi pole selles, et Java ise oleks halb keel – kunagi kasutasin seda isegi ühe huvitava aknarakenduse juurutamiseks, mis realiseeris kodumaise programmeeritava kalkulaatori funktsionaalsust (kursuseprojekti raames). Ja süntaksi poolest on see väga sarnane sama C-sharpiga. Noh, see on ainult pluss: seda lihtsam on mul olemasolevat projekti lõpule viia. Kuid ma ei tahtnud uuesti sukelduda sellesse üsna ebatavalisse Java-tehnoloogiate akna- (või õigemini töölaua-) maailma - keel ise ei olnud ju selliseks kasutamiseks “kohandatud” ja ma ei ihaldanud üldse selle kordamist. eelnev kogemus. Võib-olla just seetõttu, et C# on koos WinFormsiga palju lähedasem Delphile, millega paljud meist kunagi alustasid. Õnneks leiti vajalik lahendus üsna kiiresti - projekti näol IKVM.NET, mis muudab olemasolevate Java-programmide tõlkimise hallatavaks .NET-koodiks lihtsaks. Tõsi, projekt ise oli selleks ajaks autorite poolt juba hüljatud, kuid selle viimane teostus võimaldas mul üsna edukalt teha vajalikke toiminguid lähtetekstide jaoks Gorp.

Seega tegin kõik vajalikud muudatused ja koostasin selle kõik sobivat tüüpi DLL-i, mida kõik Visual Studios loodud .NET Frameworki projektid saaksid hõlpsasti “üle võtta”. Vahepeal lõin tagastatud tulemuste mugavaks esitlemiseks veel ühe kihi Gorp, vastavate andmestruktuuride kujul, mida oleks mugav tabelivaates töödelda (võttes aluseks nii read kui veerud; nii sõnastiku võtmed kui ka numbriindeksid). Noh, vajalikud utiliidid ise tulemuste töötlemiseks ja kuvamiseks said üsna kiiresti kirjutatud.

Samuti ei tekitanud erilisi komplikatsioone uue mootori jaoks mallide kohandamise protsess, et õpetada seda sisukorra skannitud tekstide olemasolevaid näidiseid sõeluma. Tegelikult ei pidanud ma isegi oma varasematele mallidele viitama: lõin kõik vajalikud mallid lihtsalt nullist. Veelgi enam, kui süsteemi eelmise versiooniga töötamiseks mõeldud mallid seadsid tekstidele, mida nende abiga saaks õigesti sõeluda, üsna kitsa raamistiku, võimaldas uus mootor juba välja töötada üsna universaalseid malle, mis sobivad mitut tüüpi märgistuse jaoks. üks kord. Üritasin isegi kirjutada mingit kõikehõlmavat malli mis tahes suvalise sisukorra teksti jaoks, kuigi loomulikult isegi siis, kui mulle avanevad kõik uued võimalused, sealhulgas eelkõige piiratud võimalus rakendada samu pesastatud korduvaid jadasid ( nagu näiteks perekonnanimed ja initsiaalid mitmel autoril järjest), osutus see utoopiaks.

Võib-olla on tulevikus võimalik rakendada teatud metamallide kontseptsiooni, mis suudab kontrollida lähteteksti vastavust mitmele saadaolevale mallile korraga ja seejärel vastavalt saadud tulemustele valida sobivaim, kasutades mingit intelligentset algoritmi. Kuid nüüd muretsesin rohkem teise küsimuse pärast. Parser nagu Gorp, vaatamata kogu oma mitmekülgsusele ja minu tehtud muudatustele, ei olnud see siiski oma olemuselt võimeline tegema ühte näiliselt lihtsat asja, mida minu enda kirjutatud parser suutis teha juba esimesest versioonist peale. Nimelt: tal oli oskus leida ja välja võtta lähtetekstist kõik fragmendid, mis vastavad õiges kohas kasutatud malli sees määratud maskile, samas ei huvitanud teda üldse, mida antud tekst nende fragmentide vahelistes tühikutes sisaldab. Siiani olen uut mootorit vaid veidi täiustanud, võimaldades sellel otsida kõiki võimalikke uusi kordusi selliste maskide antud jada praegusest asukohast, jättes võimaluse tekstis esineda suvaliste tähemärkide komplektid, mis olid täiesti olemas. parsimisel arvestamata, suletud korduvate struktuuride vahele. See aga ei võimaldanud määrata järgmist maski sõltumata eelmise fragmendi vastava maski abil otsimise tulemustest: kirjeldatud tekstistruktuuri rangus ei jätnud siiski ruumi ebaregulaarsete märkide suvalisteks lisamisteks.

Ja kui sisukorra näidete puhul, millega ma kokku puutusin, ei tundunud see probleem veel nii tõsine, siis kui proovite rakendada uut sõelumismehhanismi sarnase veebisaidi sisu sõelumise ülesande jaoks (st sama sõelumine), piirangud on siin, nad ilmusid kogu oma ilmsusega. On ju üsna lihtne seada vajalikud maskid veebimärgistuse fragmentidele, mille vahel peaksid asuma otsitavad andmed (mis vajavad väljavõtmist), aga kuidas sundida parserit kohe järgmise juurde liikuma. sarnane fragment, hoolimata kõigist võimalikest siltidest ja HTML-i atribuutidest, mida saab paigutada nendevahelistesse tühikutesse?

Pärast veidi mõtlemist otsustasin tutvustada paari teenindusmustrit (%all_enne) и (%all_after), mille eesmärk on tagada, et kõik, mis võib lähtetekstis sisalduda, jäetakse vahele enne mis tahes mustrit (maski), mis neile järgneb. Veelgi enam, kui (%all_enne) lihtsalt ignoreeris kõiki neid suvalisi lisamisi (%all_after), vastupidi, võimaldas need pärast eelmiselt fragmendilt liikumist soovitud fragmendile lisada. See kõlab üsna lihtsalt, kuid selle kontseptsiooni rakendamiseks pidin uuesti läbi kammima gorp-allikaid, et teha vajalikud muudatused, et mitte rikkuda juba rakendatud loogikat. Lõpuks saime sellega hakkama (kuigi isegi minu parseri kõige-kõige esimene, ehkki väga lollakas teostus sai kirja ja veel kiiremini - paari nädalaga). Nüüdsest võttis süsteem tõeliselt universaalse kuju – mitte vähem kui 12 aastat pärast esimesi katseid see toimima panna.

Muidugi pole see meie unistuste lõpp. Samuti saate gorpi malli parseri täielikult C#-s ümber kirjutada, kasutades tasuta grammatika rakendamiseks mis tahes saadaolevaid teeke. Arvan, et koodi tuleks oluliselt lihtsustada ja see võimaldab meil vabaneda pärandist olemasolevate Java allikate näol. Kuid olemasoleva mootoritüübiga on täiesti võimalik teha ka mitmesuguseid huvitavaid asju, sealhulgas katse rakendada juba mainitud metamalle, rääkimata erinevate andmete sõelumisest erinevatelt veebisaitidelt (samas ma ei välista et olemasolevad spetsiaalsed tarkvaratööriistad on selleks sobivamad – mul lihtsalt pole veel vastavat kasutuskogemust olnud).

Muide, sel suvel sain juba meili teel kutse ühelt Salesforce tehnoloogiaid kasutavalt ettevõttelt (algse versiooni arendaja Gorp), läbida intervjuu edasiseks tööks Riias. Kahjuks ei ole ma hetkel sellisteks ümberpaigutusteks valmis.

Kui see materjal tekitab mõningast huvi, siis teises osas proovin täpsemalt kirjeldada mallide koostamise ja hilisema parsimise tehnoloogiat Salesforce’is kasutatava juurutuse näitel. Gorp (minu enda täiendused, välja arvatud paar juba kirjeldatud funktsioonisõna, ei muuda praktiliselt mingeid muudatusi malli süntaksis, seega peaaegu kogu algse süsteemi dokumentatsioon Gorp Sobib ka minu versioonile).

Allikas: www.habr.com

Lisa kommentaar