Historien om et lille projekt tolv år langt (om BIRMA.NET for første gang og ærligt talt førstehånds)

Fødslen af ​​dette projekt kan betragtes som en lille idé, der kom til mig et sted i slutningen af ​​2007, som var bestemt til at finde sin endelige form først 12 år senere (på dette tidspunkt - selvfølgelig, selvom den nuværende implementering iflg. til forfatteren, er meget tilfredsstillende).

Det hele startede, da jeg i forbindelse med udførelsen af ​​mine daværende officielle pligter i biblioteket gjorde opmærksom på, at processen med at indtaste data fra den scannede tekst af indholdsfortegnelser for bog- (og musik-) publikationer i den eksisterende database, tilsyneladende kan forenkles betydeligt og automatiseres, ved at drage fordel af egenskaben af ​​orden og repeterbarhed af alle data, der kræves til input, såsom navnet på forfatteren af ​​artiklen (hvis vi taler om en samling af artikler), titlen på artiklen (eller undertitlen afspejlet i indholdsfortegnelsen) og sidenummeret på den aktuelle indholdsfortegnelse. Først var jeg praktisk talt overbevist om, at et system, der var egnet til at udføre denne opgave, nemt kunne findes på internettet. Da en overraskelse var forårsaget af det faktum, at jeg ikke kunne finde sådan et projekt, besluttede jeg at prøve at implementere det på egen hånd.

Efter ganske kort tid begyndte den første prototype at virke, som jeg straks begyndte at bruge i mine daglige aktiviteter, samtidig med at debuggede den på alle de eksempler, der kom til min hånd. På min sædvanlige arbejdsplads, hvor jeg på ingen måde var programmør, slap jeg heldigvis alligevel afsted med synlig "nedetid" i mit arbejde, hvor jeg intensivt debuggede mit hjernebarn - en næsten utænkelig ting i den nuværende virkelighed, hvilket indebærer daglige rapporter om arbejde udført i løbet af dagen. Processen med at polere programmet tog i alt ikke mindre end omkring et år, men selv derefter kunne resultatet næppe kaldes helt vellykket - der blev i starten lagt for mange forskellige koncepter, som ikke var helt klare til implementering: valgfrie elementer, der kan blive sprunget over; fremadvisning af elementer (med det formål at erstatte tidligere elementer i søgeresultater); selv vores eget forsøg på at implementere noget som regulære udtryk (som har en unik syntaks). Jeg må sige, at før dette havde jeg opgivet programmering noget (i ca. 8 år, hvis ikke mere), så den nye mulighed for at anvende mine færdigheder til en interessant og nødvendig opgave fangede fuldstændig min opmærksomhed. Det er ikke overraskende, at den resulterende kildekode - i mangel af nogen klare tilgange til dets design fra min side - ret hurtigt blev en ufattelig blanding af forskellige stykker i C-sproget med nogle elementer af C++ og aspekter af visuel programmering (i første omgang det blev besluttet at bruge et sådant designsystem som Borland C++ Builder - "næsten Delphi, men i C"). Men alt dette bar i sidste ende frugt ved at automatisere de daglige aktiviteter på vores bibliotek.

Samtidig besluttede jeg, for en sikkerheds skyld, at tage kurser for at uddanne professionelle softwareudviklere. Jeg ved ikke, om det rent faktisk er muligt at lære "at være programmør" fra bunden der, men taget i betragtning af de færdigheder, jeg allerede havde på det tidspunkt, var jeg i stand til at mestre teknologier, der var mere relevante på det tidspunkt, f.eks. som C#, Visual Studio til udvikling under .NET, samt nogle teknologier relateret til Java, HTML og SQL. Hele uddannelsen tog i alt to år, og fungerede som udgangspunkt for et andet projekt af mig, som i sidste ende strakte sig over flere år - men dette er et emne for en separat publikation. Her vil det kun være på sin plads at bemærke, at jeg gjorde et forsøg på at tilpasse den udvikling, jeg allerede havde på det beskrevne projekt, til at skabe en fuldgyldig vinduesapplikation i C# og WinForms, der implementerer den nødvendige funktionalitet, og bruge den som grundlag for kommende diplomprojekt.
Med tiden begyndte denne idé at forekomme mig værdig til at blive udtrykt ved sådanne årlige konferencer med deltagelse af repræsentanter for forskellige biblioteker som "LIBKOM" og "CRIMEA". Ideen, ja, men ikke min implementering af den på det tidspunkt. Så håbede jeg også, at nogen ville omskrive det med mere kompetente tilgange. På den ene eller anden måde besluttede jeg i 2013 at skrive en rapport om mit foreløbige arbejde og sende den til konferencens organisationskomité med en ansøgning om tilskud til at deltage i konferencen. Til min lidt overraskelse blev min ansøgning godkendt, og jeg begyndte at lave nogle forbedringer af projektet for at forberede det til præsentation på konferencen.

På det tidspunkt havde projektet allerede fået et nyt navn BIRMA, erhvervet forskellige yderligere (ikke så meget fuldt implementerede, men snarere antaget) kapaciteter - alle detaljer kan findes i min rapport.

For at være ærlig var det svært at kalde BIRMA 2013 noget komplet; Helt ærligt, det var et meget hacket håndværk lavet i hast. Med hensyn til kode var der praktisk talt ingen særlige innovationer overhovedet, bortset fra et ret hjælpeløst forsøg på at skabe en form for ensartet syntaks for parseren, i udseende, der minder om IRBIS 64-formateringssproget (og faktisk også ISIS-systemet - med parenteser som cykliske strukturer; hvorfor på det tidspunkt syntes jeg, det så ret fedt ud). Parseren faldt håbløst over disse cirkler af parenteser af den passende type (da parenteser også spillede en anden rolle, nemlig de markerede valgfrie strukturer under parsing, som kan springes over). Jeg henviser igen alle, der ønsker at stifte bekendtskab med den dengang vanskeligt forestillede, uberettigede syntaks af BIRMA mere detaljeret til min beretning fra dengang.

Generelt, bortset fra at jeg kæmper med min egen parser, har jeg ikke mere at sige om koden for denne version - bortset fra den omvendte konvertering af de eksisterende kilder til C++, mens jeg bevarer nogle typiske træk ved .NET-kode (for at være ærlig, så er det svært at forstå, hvad der præcist fik mig til at flytte alt tilbage - sandsynligvis en eller anden dum frygt for at holde mine kildekoder hemmelige, som om det var noget svarende til den hemmelige opskrift på Coca-Cola).

Måske er denne dumme beslutning også årsagen til vanskelighederne med at parre det resulterende DLL-bibliotek med den eksisterende grænseflade på en hjemmelavet arbejdsstation til at indtaste data i et elektronisk katalog (ja, jeg nævnte ikke en anden vigtig kendsgerning: fra nu af vil alle koden for BIRMA "motoren" var som forventet, den er adskilt fra grænsefladedelen og pakket i den relevante DLL). Hvorfor det var nødvendigt at skrive en separat arbejdsstation til disse formål, som alligevel i sit udseende og metode til interaktion med brugeren skamløst kopierede den samme arbejdsstation "Catalogizer" af IRBIS 64-systemet - dette er et separat spørgsmål. Kort sagt: det gav den nødvendige soliditet til min daværende udvikling til mit afgangsprojekt (ellers var den ufordøjelige parsermotor alene på en eller anden måde ikke nok). Derudover stødte jeg så på nogle vanskeligheder med at implementere grænsefladen til Cataloger-arbejdsstationen med mine egne moduler, implementeret i både C++ og C#, og få direkte adgang til min motor.

Generelt var det mærkeligt nok denne ret klodsede prototype af fremtidens BIRMA.NET, der var bestemt til at blive min "arbejdshest" i de næste fire år. Det kan ikke siges, at jeg i løbet af denne tid ikke i det mindste forsøgte at finde måder til en ny, mere komplet implementering af en langvarig idé. Blandt andre innovationer skulle der allerede have været indlejrede cykliske sekvenser, der kunne have inkluderet valgfrie elementer - det var sådan, jeg ville føre ideen om universelle skabeloner til bibliografiske beskrivelser af publikationer og forskellige andre interessante ting ud i livet. Men i mine praktiske aktiviteter på det tidspunkt var alt dette ikke efterspurgt, og den implementering, jeg havde på det tidspunkt, var ganske tilstrækkelig til at indtaste indholdsfortegnelser. Derudover begyndte udviklingsvektoren for vores bibliotek at afvige mere og mere i retning af digitalisering af museumsarkiver, rapportering og andre aktiviteter af ringe interesse for mig, hvilket i sidste ende tvang mig til endelig at forlade det og give plads til dem, der ville være mere tilfreds med alt dette.

Paradoksalt nok var det efter disse dramatiske begivenheder, at BIRMA-projektet, som allerede på det tidspunkt havde alle de karakteristiske træk ved et typisk langsigtet byggeprojekt, så ud til at begynde at få sit længe ventede nye liv! Jeg havde mere fritid til ledige tanker, jeg begyndte igen at finkæmme World Wide Web på jagt efter noget lignende (heldigvis, nu kunne jeg allerede gætte at lede efter alt dette ikke bare hvor som helst, men på GitHub), og et sted i At begyndelsen af ​​dette år stødte jeg endelig på et tilsvarende produkt fra det kendte Salesforce-firma under det ubetydelige navn Gorp. I sig selv kunne den næsten alt, hvad jeg havde brug for fra sådan en parsermotor - nemlig intelligent isolere individuelle fragmenter fra vilkårlig, men klart struktureret tekst, samtidig med at den havde en ret brugervenlig grænseflade for slutbrugeren, inklusive sådanne forståelige essenser, som et mønster, skabelon og forekomst, og samtidig bruge regulære udtryks velkendte syntaks, som bliver uforlignelig mere læsbar på grund af opdelingen i udpegede semantiske grupper til parsing.

Generelt besluttede jeg, at dette er den ene Gorp (Jeg spekulerer på, hvad dette navn betyder? Måske en slags "generelt orienteret regulær parser"?) - præcis hvad jeg har ledt efter i lang tid. Sandt nok havde dens umiddelbare implementering til mine egne behov et sådant problem, at denne motor krævede for streng overholdelse af kildetekstens strukturelle rækkefølge. For nogle rapporter såsom logfiler (nemlig de blev placeret af udviklerne som klare eksempler på brug af projektet), er dette ganske velegnet, men for de samme tekster af scannede indholdsfortegnelser er det usandsynligt. Når alt kommer til alt, kan den samme side med en indholdsfortegnelse begynde med ordene "Indholdsfortegnelse", "Indhold" og eventuelle andre foreløbige beskrivelser, som vi ikke behøver at placere i resultaterne af den påtænkte analyse (og skære dem af manuelt hver gang er også ubelejligt). Derudover kan siden mellem individuelle gentagne elementer, såsom forfatterens navn, titel og sidenummer, indeholde en vis mængde affald (f.eks. tegninger og blot tilfældige tegn), hvilket det også ville være rart at kunne skære af. Det sidste aspekt var dog endnu ikke så væsentligt, men på grund af det første kunne den eksisterende implementering ikke begynde at lede efter de nødvendige strukturer i teksten fra et bestemt sted, men i stedet behandlede den blot fra begyndelsen, fandt ikke specificerede mønstre der og... endte mit job. Det er klart, at der var behov for nogle justeringer for i det mindste at tillade lidt plads mellem de gentagne strukturer, og det fik mig tilbage på arbejde.

Et andet problem var, at selve projektet blev implementeret i Java, og hvis jeg i fremtiden planlagde at implementere nogle midler til at interface denne teknologi med velkendte applikationer til indtastning af data i eksisterende databaser (såsom Irbis' "Cataloguer"), så i det mindste i det mindste gør dette i C# og .NET. Det er ikke, at Java i sig selv er et dårligt sprog - jeg brugte det engang engang til at implementere en interessant vinduesapplikation, der implementerede funktionaliteten af ​​en indenlandsk programmerbar lommeregner (som en del af et kursusprojekt). Og syntaksmæssigt minder den meget om den samme C-sharp. Nå, dette er kun et plus: jo lettere bliver det for mig at færdiggøre et eksisterende projekt. Jeg ønskede dog ikke at kaste mig ud i denne ret usædvanlige verden af ​​vindues- (eller rettere, desktop) Java-teknologier - trods alt var sproget i sig selv ikke "skræddersyet" til sådan brug, og jeg havde slet ikke lyst til en gentagelse af den tidligere erfaring. Måske er det netop fordi C# i forbindelse med WinForms er meget tættere på Delphi, som mange af os engang startede med. Heldigvis blev den nødvendige løsning fundet ret hurtigt – i form af projektet IKVM.NET, hvilket gør det nemt at oversætte eksisterende Java-programmer til administreret .NET-kode. Sandt nok var selve projektet allerede blevet forladt af forfatterne på det tidspunkt, men dets seneste implementering gjorde det muligt for mig at udføre de nødvendige handlinger for kildeteksterne med succes. Gorp.

Så jeg lavede alle de nødvendige ændringer og samlede det hele til en DLL af den passende type, som nemt kunne "samles" af alle projekter til .NET Framework oprettet i Visual Studio. I mellemtiden har jeg oprettet et andet lag til bekvem præsentation af de returnerede resultater Gorp, i form af tilsvarende datastrukturer, der ville være praktiske at behandle i en tabelvisning (med udgangspunkt i både rækker og kolonner; både ordbogsnøgler og numeriske indekser). Nå, selve de nødvendige værktøjer til behandling og visning af resultaterne blev skrevet ret hurtigt.

Processen med at tilpasse skabeloner til den nye motor for at lære den at parse eksisterende prøver af scannede tekster af indholdsfortegnelser forårsagede heller ikke særlige komplikationer. Faktisk behøvede jeg slet ikke at henvise til mine tidligere skabeloner: Jeg oprettede simpelthen alle de nødvendige skabeloner fra bunden. Desuden, hvis skabelonerne designet til at fungere med den tidligere version af systemet satte en ret snæver ramme for tekster, der kunne parses korrekt med deres hjælp, har den nye motor allerede gjort det muligt at udvikle temmelig universelle skabeloner, der egner sig til flere typer markup på enkelt gang. Jeg forsøgte endda at skrive en form for omfattende skabelon til enhver vilkårlig indholdsfortegnelse tekst, selv om selvfølgelig selv med alle de nye muligheder, der åbnede sig for mig, herunder især den begrænsede evne til at implementere de samme indlejrede gentagne sekvenser ( som for eksempel efternavne og initialer flere forfattere i træk), viste det sig at være en utopi.

Måske vil det i fremtiden være muligt at implementere et bestemt koncept af meta-skabeloner, som vil være i stand til at kontrollere kildeteksten for overensstemmelse med flere af de tilgængelige skabeloner på én gang, og derefter, i overensstemmelse med de opnåede resultater, vælge mest egnede ved at bruge en slags intelligent algoritme. Men nu var jeg mere bekymret over et andet spørgsmål. En parser som Gorp, på trods af al dens alsidighed og de ændringer, jeg lavede, var den stadig i sagens natur ude af stand til at gøre en tilsyneladende simpel ting, som min selvskrevne parser var i stand til fra den allerførste version. Nemlig: han havde evnen til at finde og uddrage fra kildeteksten alle fragmenter, der matcher den maske, der er angivet i den anvendte skabelon på det rigtige sted, samtidig med at han slet ikke var interesseret i, hvad den givne tekst indeholder i mellemrummene mellem disse fragmenter. Indtil videre har jeg kun en smule forbedret den nye motor, så den kan søge efter alle mulige nye gentagelser af en given sekvens af sådanne masker fra den aktuelle position, hvilket giver mulighed for tilstedeværelsen i teksten af ​​sæt af vilkårlige tegn, der var fuldstændigt der ikke tages højde for i parsingen, indesluttet mellem de detekterede gentagne strukturer. Dette gjorde det dog ikke muligt at indstille den næste maske uanset resultaterne af søgningen efter det forrige fragment ved hjælp af den tilsvarende maske: strengheden af ​​den beskrevne tekststruktur gav stadig ikke plads til vilkårlige inklusion af uregelmæssige tegn.

Og hvis for eksemplerne på indholdsfortegnelser, som jeg stødte på, dette problem endnu ikke virkede så alvorligt, så når man forsøger at anvende en ny parsingmekanisme til en lignende opgave med at parse indholdet af et websted (dvs. den samme parsing), begrænsninger er her, de dukkede op med al deres åbenhed. Det er trods alt ret nemt at indstille de nødvendige masker for fragmenter af webmarkering, mellem hvilke de data, vi leder efter (som skal udtrækkes), skal være placeret, men hvordan kan vi tvinge parseren til straks at gå videre til den næste lignende fragment, på trods af alle de mulige tags og HTML-attributter, der kan placeres i mellemrummene mellem dem?

Efter at have tænkt lidt, besluttede jeg at introducere et par servicemønstre (%alle_før) и (%alt_efter), der tjener det åbenlyse formål at sikre, at alt, hvad der kan være indeholdt i kildeteksten, springes over før ethvert mønster (maske), der følger dem. Desuden, hvis (%alle_før) simpelthen ignoreret alle disse vilkårlige indeslutninger, så (%alt_efter), tværtimod, tillod dem at blive tilføjet til det ønskede fragment efter flytning fra det forrige fragment. Det lyder ret simpelt, men for at implementere dette koncept var jeg nødt til at finkæmme gorp-kilderne igen for at foretage de nødvendige ændringer for ikke at bryde den allerede implementerede logik. Til sidst lykkedes det os at gøre dette (selvom selv den allerførste, omend meget buggy, implementering af min parser blev skrevet, og endnu hurtigere - på et par uger). Fra nu af fik systemet en virkelig universel form - ikke mindre end 12 år efter de første forsøg på at få det til at fungere.

Selvfølgelig er dette ikke enden på vores drømme. Du kan også fuldstændigt omskrive gorp-skabelonparseren i C# ved at bruge et hvilket som helst af de tilgængelige biblioteker til at implementere en gratis grammatik. Jeg synes, koden skal forenkles betydeligt, og det vil give os mulighed for at slippe af med arven i form af eksisterende Java-kilder. Men med den eksisterende type motor er det også meget muligt at lave forskellige interessante ting, herunder et forsøg på at implementere de meta-skabeloner, jeg allerede har nævnt, for ikke at tale om at parse forskellige data fra forskellige websteder (dog udelukker jeg ikke at eksisterende specialiserede softwareværktøjer er mere egnede til dette – jeg har bare ikke haft den passende erfaring med at bruge dem endnu).

Forresten, denne sommer modtog jeg allerede en invitation på e-mail fra et firma, der bruger Salesforce-teknologier (udvikleren af ​​den originale Gorp), bestå en samtale til efterfølgende arbejde i Riga. Desværre er jeg i øjeblikket ikke klar til sådanne omplaceringer.

Hvis dette materiale vækker en vis interesse, så vil jeg i anden del forsøge at beskrive mere detaljeret teknologien til kompilering og efterfølgende parsing af skabeloner ved hjælp af eksemplet på implementeringen brugt i Salesforce Gorp (mine egne tilføjelser, med undtagelse af et par funktionsord, der allerede er beskrevet, foretager stort set ingen ændringer i selve skabelonsyntaksen, så næsten al dokumentation for det originale system Gorp Passer også til min version).

Kilde: www.habr.com

Tilføj en kommentar