Berättelsen om ett litet projekt som är tolv år långt (om BIRMA.NET för första gången och ärligt talat från första hand)

Födelsen av detta projekt kan betraktas som en liten idé som kom till mig någonstans i slutet av 2007, som var avsedd att hitta sin slutgiltiga form först 12 år senare (vid denna tidpunkt - naturligtvis, även om den nuvarande implementeringen, enligt till författaren, är mycket tillfredsställande).

Allt började när jag, i färd med att fullgöra mina dåvarande officiella uppgifter i biblioteket, uppmärksammade det faktum att processen att mata in data från den skannade texten av innehållsförteckningar för bok- (och musik) publikationer i den befintliga databasen, uppenbarligen kan avsevärt förenklas och automatiseras genom att dra nytta av egenskapen ordning och repeterbarhet av all data som krävs för inmatning, såsom namnet på författaren till artikeln (om vi pratar om en samling artiklar), titeln på artikeln (eller undertiteln som återspeglas i innehållsförteckningen) och sidnumret för den aktuella innehållsförteckningen. Först var jag praktiskt taget övertygad om att ett system som lämpar sig för att utföra denna uppgift lätt kunde hittas på Internet. När en överraskning orsakades av att jag inte kunde hitta ett sådant projekt, bestämde jag mig för att försöka genomföra det på egen hand.

Efter en ganska kort tid började den första prototypen att fungera, som jag omedelbart började använda i mina dagliga aktiviteter, samtidigt som jag felsökte den på alla exempel som kom till min hand. Lyckligtvis, på min vanliga arbetsplats, där jag inte på något sätt var programmerare, kom jag ändå undan med synliga "stopptid" i mitt arbete, under vilken jag intensivt felsökte min idé - en nästan otänkbar sak i den nuvarande verkligheten, vilket innebär dagliga rapporter om arbete som utförts under dagen. Processen med att polera programmet tog totalt inte mindre än cirka ett år, men även efter det kunde resultatet knappast kallas helt lyckat – det lades till en början upp för många olika koncept som inte var helt tydliga för implementering: valfria element som kan hoppa över; framåtvisning av element (i syfte att ersätta tidigare element i sökresultat); även vårt eget försök att implementera något som reguljära uttryck (som har en unik syntax). Jag måste säga att innan detta hade jag gett upp programmeringen något (i ungefär 8 år, om inte mer), så den nya möjligheten att tillämpa mina kunskaper på en intressant och nödvändig uppgift fångade min uppmärksamhet helt. Det är inte förvånande att den resulterande källkoden - i avsaknad av några tydliga tillvägagångssätt till dess design från min sida - ganska snabbt blev en ofattbar sammanblandning av olika delar i C-språket med vissa element av C++ och aspekter av visuell programmering (inledningsvis beslutade att använda ett sådant designsystem som Borland C++ Builder - "nästan Delphi, men i C"). Men allt detta bar slutligen frukt i att automatisera de dagliga aktiviteterna på vårt bibliotek.

Samtidigt bestämde jag mig för att för säkerhets skull gå kurser för att utbilda professionella mjukvaruutvecklare. Jag vet inte om det faktiskt är möjligt att lära sig "att vara programmerare" från grunden där, men med tanke på de färdigheter jag redan hade vid den tiden, kunde jag lite behärska teknologier som var mer relevanta vid den tiden, t.ex. som C#, Visual Studio för utveckling under .NET, samt vissa teknologier relaterade till Java, HTML och SQL. Hela utbildningen tog totalt två år och fungerade som startpunkten för ett annat projekt av mig, som i slutändan sträckte sig över flera år - men detta är ett ämne för en separat publikation. Här skulle det bara vara lämpligt att notera att jag gjorde ett försök att anpassa den utveckling jag redan hade på det beskrivna projektet för att skapa en fullfjädrad fönsterapplikation i C# och WinForms som implementerar nödvändig funktionalitet, och använda den som grund för kommande diplomprojekt.
Med tiden började denna idé verka för mig värdig att uttryckas vid sådana årliga konferenser med deltagande av representanter för olika bibliotek som "LIBKOM" och "CRIMEA". Idén, ja, men inte mitt genomförande av det på den tiden. Sedan hoppades jag också att någon skulle skriva om den med mer kompetenta metoder. På ett eller annat sätt bestämde jag mig för att 2013 skriva en rapport om mitt förberedande arbete och skicka den till konferensens organisationskommitté med en ansökan om bidrag för att delta i konferensen. Till min något förvåning godkändes min ansökan och jag började göra några förbättringar av projektet för att förbereda det för presentation på konferensen.

Vid den tiden hade projektet redan fått ett nytt namn BIRMA, förvärvat olika ytterligare (inte så mycket fullt implementerade, utan snarare antagna) förmågor - alla detaljer finns i min rapport.

För att vara ärlig var det svårt att kalla BIRMA 2013 för något komplett; Ärligt talat var det ett väldigt hackigt hantverk som gjordes i all hast. När det gäller kod fanns det praktiskt taget inga speciella innovationer alls, förutom ett ganska hjälplöst försök att skapa någon slags enhetlig syntax för parsern, i utseende som påminner om formateringsspråket IRBIS 64 (och faktiskt också ISIS-systemet - med parenteser som cykliska strukturer; varför Jag tyckte då att det såg ganska coolt ut). Parsern snubblade hopplöst på dessa cirklar av parenteser av lämplig typ (eftersom parenteser också spelade en annan roll, nämligen de markerade valfria strukturer under analysen som kan hoppas över). Jag hänvisar återigen alla som vill bekanta sig med BIRMAs då svåröverskådliga, omotiverade syntax mer i detalj till min dåtida rapport.

I allmänhet, förutom att jag kämpar med min egen parser, har jag inget mer att säga om koden för den här versionen - förutom den omvända konverteringen av befintliga källor till C++ samtidigt som jag bevarar några typiska egenskaper hos .NET-koden (för att vara ärlig så är det svårt att förstå, exakt vad som fick mig att flytta tillbaka allt - förmodligen en dum rädsla för att hålla mina källkoder hemliga, som om det vore något motsvarande det hemliga receptet från Coca-Cola).

Kanske är detta dumma beslut också orsaken till svårigheterna med att para ihop det resulterande DLL-biblioteket med det befintliga gränssnittet på en hemmagjord arbetsstation för att mata in data i en elektronisk katalog (ja, jag nämnde inte ett annat viktigt faktum: från och med nu, alla koden för BIRMA "motorn" var som förväntat, den är separerad från gränssnittsdelen och förpackad i lämplig DLL). Varför det var nödvändigt att skriva en separat arbetsstation för dessa ändamål, som hur som helst, i sitt utseende och metod för interaktion med användaren, skamlöst kopierade samma arbetsstation "Catalogizer" av IRBIS 64-systemet - det här är en separat fråga. Kort sagt: det gav den nödvändiga soliditeten till min dåvarande utveckling för mitt examensprojekt (annars räckte den svårsmälta parsermotorn ensam på något sätt inte). Dessutom stötte jag på en del svårigheter med att implementera gränssnittet för Cataloger-arbetsstationen med mina egna moduler, implementerade i både C++ och C#, och direkt komma åt min motor.

I allmänhet var det konstigt nog denna ganska klumpiga prototyp av det framtida BIRMA.NET som var avsett att bli min "arbetshäst" under de kommande fyra åren. Det kan inte sägas att jag under denna tid inte åtminstone försökte hitta vägar för en ny, mer komplett implementering av en långvarig idé. Bland andra innovationer borde det redan ha funnits kapslade cykliska sekvenser som kunde ha inkluderat valfria element - det är så jag tänkte levandegöra idén om universella mallar för bibliografiska beskrivningar av publikationer och diverse andra intressanta saker. Men i mina praktiska aktiviteter på den tiden var allt detta efterfrågat litet, och den implementering jag hade vid den tiden var ganska tillräcklig för att skriva in innehållsförteckningar. Dessutom började utvecklingsvektorn för vårt bibliotek att avvika mer och mer mot digitalisering av museiarkiv, rapportering och andra aktiviteter av ringa intresse för mig, vilket i slutändan tvingade mig att slutligen lämna det och ge vika för dem som skulle vara mer nöjd med allt detta.

Paradoxalt nog var det efter dessa dramatiska händelser som BIRMA-projektet, som vid den tiden redan hade alla karaktäristiska drag av ett typiskt långsiktigt byggprojekt, tycktes börja ta sitt efterlängtade nya liv! Jag hade mer ledig tid för lediga tankar, jag började återigen kamma World Wide Web på jakt efter något liknande (lyckligtvis, nu kunde jag redan gissa att jag skulle leta efter allt detta inte bara var som helst, utan på GitHub), och någonstans i At the i början av detta år kom jag äntligen över en motsvarande produkt från det välkända Salesforce-företaget under det obetydliga namnet Gorp. I sig själv kunde den göra nästan allt som jag behövde från en sådan parsermotor - nämligen intelligent isolera enskilda fragment från godtycklig, men tydligt strukturerad text, samtidigt som den har ett ganska användarvänligt gränssnitt för slutanvändaren, inklusive sådana begripliga essenser, som ett mönster, mall och förekomst, och samtidigt använda den välbekanta syntaxen för reguljära uttryck, som blir ojämförligt mer läsbar på grund av uppdelningen i angivna semantiska grupper för analys.

I allmänhet bestämde jag mig för att det är den här Gorp (Jag undrar vad det här namnet betyder? Kanske någon slags "generellt orienterad vanlig parser"?) – precis vad jag har letat efter länge. Det är sant att dess omedelbara implementering för mina egna behov hade ett sådant problem att denna motor krävde alltför strikt följsamhet till källtextens strukturella sekvens. För vissa rapporter som loggfiler (de placerades nämligen av utvecklarna som tydliga exempel på att använda projektet) är detta ganska lämpligt, men för samma texter av skannade innehållsförteckningar är det osannolikt. Trots allt kan samma sida med en innehållsförteckning börja med orden "Innehållsförteckning", "Innehåll" och eventuella andra preliminära beskrivningar som vi inte behöver placera i resultaten av den avsedda analysen (och skära bort dem manuellt varje gång är också obekvämt). Dessutom, mellan enskilda återkommande element, såsom författarens namn, titel och sidnummer, kan sidan innehålla en viss mängd skräp (till exempel teckningar och bara slumpmässiga tecken), vilket det också skulle vara trevligt att kunna avskuren. Den sista aspekten var dock ännu inte så betydelsefull, men på grund av den första kunde den befintliga implementeringen inte börja leta efter de nödvändiga strukturerna i texten från en viss plats, utan istället bearbetade den helt enkelt från början, hittade inte specificerade mönster där och... avslutade mitt jobb. Uppenbarligen behövdes en del justeringar för att åtminstone tillåta lite utrymme mellan de upprepade strukturerna, och det fick mig tillbaka till jobbet.

Ett annat problem var att själva projektet implementerades i Java, och om jag i framtiden planerade att implementera någon form av gränssnitt för denna teknik med välbekanta applikationer för att mata in data i befintliga databaser (som Irbis "Cataloguer"), då åtminstone åtminstone gör detta i C# och .NET. Det är inte så att Java i sig är ett dåligt språk – jag använde det en gång till och med för att implementera en intressant fönsterapplikation som implementerade funktionen hos en inhemsk programmerbar kalkylator (som en del av ett kursprojekt). Och syntaxmässigt är den väldigt lik samma C-sharp. Tja, detta är bara ett plus: desto lättare blir det för mig att slutföra ett befintligt projekt. Jag ville dock inte kasta mig in i denna ganska ovanliga värld av fönster- (eller snarare skrivbords-) Java-teknik igen - trots allt var språket i sig inte "skräddarsytt" för sådan användning, och jag längtade inte alls efter en upprepning av den tidigare erfarenheten. Kanske är det just för att C# i kombination med WinForms ligger mycket närmare Delphi, som många av oss en gång började med. Lyckligtvis hittades den nödvändiga lösningen ganska snabbt - i form av projektet IKVM.NET, vilket gör det enkelt att översätta befintliga Java-program till hanterad .NET-kod. Visserligen hade själva projektet redan övergetts av författarna vid den tiden, men dess senaste implementering tillät mig att ganska framgångsrikt utföra de nödvändiga åtgärderna för källtexterna Gorp.

Så jag gjorde alla nödvändiga ändringar och satte ihop det hela till en DLL av lämplig typ, som lätt kunde "plockas upp" av alla projekt för .NET Framework som skapats i Visual Studio. Under tiden skapade jag ett annat lager för bekväm presentation av de returnerade resultaten Gorp, i form av motsvarande datastrukturer som skulle vara bekväma att bearbeta i en tabellvy (med utgångspunkt från både rader och kolumner; både ordboksnycklar och numeriska index). Tja, de nödvändiga verktygen själva för att bearbeta och visa resultaten skrevs ganska snabbt.

Processen att anpassa mallar för den nya motorn för att lära den att analysera befintliga prov av skannade innehållsförteckningstexter orsakade inte heller några speciella komplikationer. Faktum är att jag inte ens behövde hänvisa till mina tidigare mallar alls: jag skapade helt enkelt alla nödvändiga mallar från grunden. Dessutom, om mallarna utformade för att fungera med den tidigare versionen av systemet satte ett ganska snävt ramverk för texter som kunde tolkas korrekt med deras hjälp, gjorde den nya motorn det redan möjligt att utveckla ganska universella mallar som lämpar sig för flera typer av uppmärkning på en gång. Jag försökte till och med skriva någon form av heltäckande mall för vilken godtycklig innehållsförteckningstext som helst, fastän, naturligtvis, även med alla nya möjligheter som öppnade sig för mig, inklusive i synnerhet den begränsade förmågan att implementera samma kapslade repeterande sekvenser ( som till exempel efternamn och initialer flera författare i rad), detta visade sig vara en utopi.

Kanske kommer det i framtiden att vara möjligt att implementera ett visst koncept av meta-mallar, som kommer att kunna kontrollera källtexten för överensstämmelse med flera av de tillgängliga mallarna på en gång, och sedan, i enlighet med de erhållna resultaten, välj mest lämplig, med någon form av intelligent algoritm. Men nu var jag mer bekymrad över en annan fråga. En parser som Gorp, trots all dess mångsidighet och de ändringar jag gjorde, var den fortfarande i sig oförmögen att göra en till synes enkel sak som min självskrivna parser kunde göra från den allra första versionen. Nämligen: han hade förmågan att hitta och extrahera från källtexten alla fragment som matchar masken som anges i mallen som används på rätt ställe, samtidigt som han inte alls var intresserad av vad den givna texten innehåller i mellanrummen mellan dessa fragment. Hittills har jag bara förbättrat den nya motorn något, så att den kan söka efter alla möjliga nya upprepningar av en given sekvens av sådana masker från den aktuella positionen, vilket lämnar möjligheten för närvaron i texten av uppsättningar av godtyckliga tecken som var helt utan redovisning i analysen, innesluten mellan de detekterade repeterande strukturerna. Detta gjorde det dock inte möjligt att ställa in nästa mask oavsett resultaten av sökningen efter det föregående fragmentet med motsvarande mask: striktheten i den beskrivna textstrukturen lämnade fortfarande inte utrymme för godtyckliga inkluderande av oregelbundna tecken.

Och om för exemplen på innehållsförteckningar som jag stötte på detta problem ännu inte verkade så allvarligt, då när man försöker använda en ny analysmekanism på en liknande uppgift att analysera innehållet på en webbplats (dvs. samma analys), begränsningar är här de dök upp med all sin självklarhet. När allt kommer omkring är det ganska enkelt att ställa in de nödvändiga maskerna för fragment av webbmarkering, mellan vilka data vi letar efter (som behöver extraheras) ska finnas, men hur kan vi tvinga parsern att omedelbart gå vidare till nästa liknande fragment, trots alla möjliga taggar och HTML-attribut som kan placeras i mellanrummen mellan dem?

Efter att ha funderat lite, bestämde jag mig för att införa ett par servicemönster (%alla_före) и (%alla_efter), som tjänar det uppenbara syftet att se till att allt som kan finnas i källtexten hoppas över före eventuella mönster (masker) som följer dem. Dessutom, om (%alla_före) ignorerade helt enkelt alla dessa godtyckliga inneslutningar, alltså (%alla_efter), tvärtom, tillät dem att läggas till det önskade fragmentet efter att ha flyttats från det föregående fragmentet. Det låter ganska enkelt, men för att implementera detta koncept var jag tvungen att kamma igenom gorp-källorna igen för att göra de nödvändiga ändringarna för att inte bryta den redan implementerade logiken. Till slut lyckades vi göra detta (även om till och med den allra, allra första, om än väldigt buggiga, implementeringen av min parser skrevs, och ännu snabbare - på ett par veckor). Från och med nu har systemet antagit en verkligt universell form - inte mindre än 12 år efter de första försöken att få det att fungera.

Naturligtvis är detta inte slutet på våra drömmar. Du kan också helt och hållet skriva om gorps mallparser i C#, med hjälp av något av de tillgängliga biblioteken för att implementera en gratis grammatik. Jag tror att koden bör förenklas avsevärt, och detta kommer att tillåta oss att bli av med arvet i form av befintliga Java-källor. Men med den befintliga typen av motor är det också fullt möjligt att göra olika intressanta saker, inklusive ett försök att implementera de meta-mallar jag redan har nämnt, för att inte tala om att analysera olika data från olika webbplatser (dock utesluter jag inte att befintliga specialiserade mjukvaruverktyg är mer lämpade för detta – jag har bara inte haft lämplig erfarenhet av att använda dem ännu).

Förresten, i somras fick jag redan en inbjudan via e-post från ett företag som använder Salesforce-teknik (utvecklaren av originalet Gorp), klara en intervju för efterföljande arbete i Riga. Tyvärr är jag för närvarande inte redo för sådana omplaceringar.

Om detta material väcker visst intresse kommer jag i den andra delen att försöka beskriva tekniken för att kompilera och därefter analysera mallar mer i detalj med hjälp av exemplet på implementeringen som används i Salesforce Gorp (mina egna tillägg, med undantag för ett par funktionsord som redan beskrivits, gör praktiskt taget inga ändringar i själva mallsyntaxen, så nästan all dokumentation för det ursprungliga systemet Gorp Lämplig för min version också).

Källa: will.com

Lägg en kommentar