Historien om et lite prosjekt som er tolv år langt (om BIRMA.NET for første gang og ærlig talt førstehånds)

Fødselen av dette prosjektet kan betraktes som en liten idé som kom til meg et sted på slutten av 2007, som var bestemt til å finne sin endelige form bare 12 år senere (på dette tidspunktet - selvfølgelig, selv om den nåværende implementeringen, iht. til forfatteren, er svært tilfredsstillende).

Det hele startet da jeg, i ferd med å utføre mine daværende offisielle plikter i biblioteket, trakk oppmerksomheten til det faktum at prosessen med å legge inn data fra den skannede teksten til innholdsfortegnelser i bok (og musikk) publikasjoner i den eksisterende databasen, tilsynelatende kan forenkles og automatiseres betydelig, og dra nytte av egenskapen til orden og repeterbarhet av alle data som kreves for input, for eksempel navnet på forfatteren av artikkelen (hvis vi snakker om en samling av artikler), tittelen på artikkelen (eller undertittelen som gjenspeiles i innholdsfortegnelsen) og sidenummeret til den gjeldende innholdsfortegnelsen. Til å begynne med var jeg praktisk talt overbevist om at et system egnet for å utføre denne oppgaven lett kunne finnes på Internett. Da en overraskelse ble forårsaket av det faktum at jeg ikke kunne finne et slikt prosjekt, bestemte jeg meg for å prøve å implementere det på egen hånd.

Etter ganske kort tid begynte den første prototypen å fungere, som jeg umiddelbart begynte å bruke i mine daglige aktiviteter, samtidig som jeg feilsøkte den på alle eksemplene jeg fikk. Heldigvis, på min vanlige arbeidsplass, hvor jeg på ingen måte var programmerer, slapp jeg da fortsatt unna med synlig "nedetid" i arbeidet mitt, hvor jeg intensivt feilsøkte hjernebarnet mitt - en nesten utenkelig ting i den nåværende virkeligheten, som innebærer daglige rapporter om arbeid utført i løpet av dagen. Prosessen med å polere programmet tok totalt ikke mindre enn omtrent et år, men selv etter det kunne neppe resultatet kalles helt vellykket - det var for mange forskjellige konsepter som ikke var helt forståelige for implementering: valgfrie elementer som kan hoppes over ; fremvisning av elementer (for det formål å erstatte tidligere elementer i søkeresultater); til og med vårt eget forsøk på å implementere noe som vanlige uttrykk (som har en unik syntaks). Jeg må si at før dette hadde jeg gitt opp programmering noe (i omtrent 8 år, om ikke mer), så den nye muligheten til å bruke ferdighetene mine til en interessant og nødvendig oppgave fanget oppmerksomheten min fullstendig. Det er ikke overraskende at den resulterende kildekoden - i mangel av noen klare tilnærminger til utformingen fra min side - ganske raskt ble en ufattelig blanding av forskjellige deler i C-språket med noen elementer av C++ og aspekter ved visuell programmering (opprinnelig det ble besluttet å bruke et slikt designsystem som Borland C++ Builder - "nesten Delphi, men i C"). Imidlertid bar alt dette til syvende og sist frukt i automatiseringen av de daglige aktivitetene til biblioteket vårt.

Samtidig bestemte jeg meg for, for sikkerhets skyld, å ta kurs for å lære opp profesjonelle programvareutviklere. Jeg vet ikke om det faktisk er mulig å lære "å være programmerer" fra bunnen av der, men tatt i betraktning de ferdighetene jeg allerede hadde på den tiden, klarte jeg å mestre teknologier som var mer relevante på den tiden, som f.eks. som C#, Visual Studio for utvikling under .NET, samt noen teknologier relatert til Java, HTML og SQL. Hele opplæringen tok til sammen to år, og fungerte som utgangspunkt for et annet prosjekt av meg, som til slutt strakte seg over flere år - men dette er et tema for en egen publikasjon. Her vil det bare være på sin plass å merke seg at jeg gjorde et forsøk på å tilpasse utviklingen jeg allerede hadde på det beskrevne prosjektet for å lage en fullverdig vindusapplikasjon i C# og WinForms som implementerer nødvendig funksjonalitet, og bruke den som grunnlag for kommende diplomprosjekt.
Over tid begynte denne ideen å virke verdig for meg å bli gitt uttrykk for på slike årlige konferanser med deltakelse av representanter for forskjellige biblioteker som "LIBKOM" og "CRIMEA". Ideen, ja, men ikke min implementering av den på den tiden. Da håpet jeg også at noen ville skrive det om med mer kompetente tilnærminger. På en eller annen måte bestemte jeg meg for å skrive en rapport om mitt foreløpige arbeid innen 2013 og sende den til konferansens organisasjonskomité med en søknad om tilskudd til å delta på konferansen. Til min litt overraskelse ble søknaden min godkjent, og jeg begynte å gjøre noen forbedringer i prosjektet for å forberede det for presentasjon på konferansen.

På det tidspunktet hadde prosjektet allerede fått et nytt navn BIRMA, skaffet seg forskjellige tilleggsfunksjoner (ikke så mye fullt implementert, men heller antatt) - alle detaljer finner du i rapporten min.

For å være ærlig var det vanskelig å kalle BIRMA 2013 noe komplett; Ærlig talt, det var et veldig hacky håndverk laget i all hast. Når det gjelder kode, var det praktisk talt ingen spesielle innovasjoner i det hele tatt, bortsett fra et ganske hjelpeløst forsøk på å lage en slags enhetlig syntaks for parseren, i utseende som minner om formateringsspråket IRBIS 64 (og faktisk også ISIS-systemet - med parenteser som sykliske strukturer; hvorfor På den tiden syntes jeg det så ganske kult ut). Parseren snublet håpløst over disse sirklene med parenteser av passende type (siden parenteser også spilte en annen rolle, nemlig de markerte valgfrie strukturer under parsing som kan hoppes over). Jeg henviser igjen alle som ønsker å bli kjent med den da vanskelige å forestille seg, uberettigede syntaksen til BIRMA mer detaljert til min rapport fra den tiden.

Generelt sett, bortsett fra å slite med min egen parser, har jeg ikke noe mer å si angående koden til denne versjonen - bortsett fra omvendt konvertering av eksisterende kilder til C++ mens jeg bevarer noen typiske funksjoner i .NET-kode (for å være ærlig, det er vanskelig å forstå, nøyaktig hva som fikk meg til å flytte alt tilbake - sannsynligvis en dum frykt for å holde kildekodene mine hemmelige, som om det var noe tilsvarende den hemmelige oppskriften til Coca-Cola).

Kanskje er denne dumme avgjørelsen også årsaken til vanskelighetene med å pare det resulterende DLL-biblioteket med det eksisterende grensesnittet til en hjemmelaget arbeidsstasjon for å legge inn data i en elektronisk katalog (ja, jeg nevnte ikke et annet viktig faktum: fra nå av vil alle koden til BIRMA "motoren" var som forventet, den er atskilt fra grensesnittdelen og pakket i riktig DLL). Hvorfor det var nødvendig å skrive en egen arbeidsstasjon for disse formålene, som uansett, i sitt utseende og metode for interaksjon med brukeren, skamløst kopierte den samme arbeidsstasjonen "Catalogizer" til IRBIS 64-systemet - dette er et eget spørsmål. Kort sagt: det ga den nødvendige soliditeten til min daværende utvikling for avgangsprosjektet mitt (ellers var den ufordøyelige parsermotoren alene på en eller annen måte ikke nok). I tillegg møtte jeg noen vanskeligheter med å implementere grensesnittet til Cataloger-arbeidsstasjonen med mine egne moduler, implementert i både C++ og C#, og få direkte tilgang til motoren min.

Generelt, merkelig nok, var det denne ganske klønete prototypen av fremtidens BIRMA.NET som var bestemt til å bli min "arbeidshest" de neste fire årene. Det kan ikke sies at jeg i løpet av denne tiden ikke i det minste prøvde å finne måter for en ny, mer fullstendig implementering av en langvarig idé. Blant andre innovasjoner burde det allerede vært nestede sykliske sekvenser som kunne ha inkludert valgfrie elementer - slik skulle jeg levendegjøre ideen om universelle maler for bibliografiske beskrivelser av publikasjoner og diverse andre interessante ting. Men i mine praktiske aktiviteter på den tiden var alt dette lite etterspurt, og implementeringen jeg hadde på den tiden var ganske tilstrekkelig for å legge inn innholdsfortegnelser. I tillegg begynte utviklingsretningen for biblioteket vårt å avvike mer og mer mot digitalisering av museumsarkiver, rapportering og andre aktiviteter av liten interesse for meg, noe som til slutt tvang meg til å forlate det, og vike for de som ville være mer fornøyd med alt dette.

Paradoksalt nok var det etter disse dramatiske hendelsene at BIRMA-prosjektet, som på den tiden allerede hadde alle de karakteristiske trekkene til et typisk langsiktig byggeprosjekt, så ut til å begynne å ta sitt etterlengtede nye liv! Jeg hadde mer ledig tid til ledige tanker, jeg begynte igjen å finkjemme World Wide Web på leting etter noe lignende (heldigvis, nå kunne jeg allerede gjette å se etter alt dette ikke bare hvor som helst, men på GitHub), og et sted i At begynnelsen av dette året kom jeg endelig over et tilsvarende produkt fra det velkjente Salesforce-selskapet under det ubetydelige navnet Gorp. I seg selv kunne den gjøre nesten alt jeg trengte fra en slik parsermotor - nemlig intelligent isolere individuelle fragmenter fra vilkårlig, men tydelig strukturert tekst, samtidig som den har et ganske brukervennlig grensesnitt for sluttbrukeren, inkludert slike forståelige essenser, som et mønster, mal og forekomst, og samtidig bruke den kjente syntaksen til regulære uttrykk, som blir uforlignelig mer lesbar på grunn av inndelingen i utpekte semantiske grupper for parsing.

Generelt bestemte jeg meg for at dette er den Gorp (Jeg lurer på hva dette navnet betyr? Kanskje en slags "generelt orientert vanlig parser"?) - akkurat det jeg har lett etter lenge. Riktignok hadde dens umiddelbare implementering for mine egne behov et slikt problem at denne motoren krevde for streng overholdelse av den strukturelle sekvensen til kildeteksten. For noen rapporter som loggfiler (nemlig de ble plassert av utviklerne som klare eksempler på bruk av prosjektet), er dette ganske passende, men for de samme tekstene til skannede innholdsfortegnelser er det usannsynlig. Tross alt kan den samme siden med en innholdsfortegnelse begynne med ordene "Innholdsfortegnelse", "Innhold" og noen andre foreløpige beskrivelser som vi ikke trenger å plassere i resultatene av den tiltenkte analysen (og kutte dem manuelt hver gang er også upraktisk). I tillegg, mellom individuelle gjentakende elementer, som forfatterens navn, tittel og sidenummer, kan siden inneholde en viss mengde søppel (for eksempel tegninger og bare tilfeldige tegn), som det også ville vært fint å kunne kuttet av. Det siste aspektet var imidlertid ikke så viktig ennå, men på grunn av det første kunne den eksisterende implementeringen ikke begynne å lete etter de nødvendige strukturene i teksten fra et bestemt sted, men behandlet den i stedet helt fra begynnelsen, fant ikke spesifiserte mønstre der og... avsluttet jobben min. Det var tydeligvis nødvendig med noen justeringer for i det minste å tillate litt plass mellom de gjentatte strukturene, og det fikk meg tilbake på jobb.

Et annet problem var at selve prosjektet ble implementert i Java, og hvis jeg i fremtiden planla å implementere noen måter å koble denne teknologien sammen med kjente applikasjoner for å legge inn data i eksisterende databaser (som Irbis sin "Cataloguer"), så i det minste gjør dette i C# og .NET. Det er ikke det at Java i seg selv er et dårlig språk – jeg brukte det en gang til å implementere en interessant vindusapplikasjon som implementerte funksjonaliteten til en innenlandsk programmerbar kalkulator (som en del av et kursprosjekt). Og når det gjelder syntaks er den veldig lik den samme C-sharpen. Vel, dette er bare et pluss: jo lettere blir det for meg å fullføre et eksisterende prosjekt. Jeg ønsket imidlertid ikke å kaste meg ut i denne ganske uvanlige verdenen av vindu (eller rettere sagt, desktop) Java-teknologier - tross alt var ikke språket i seg selv "skreddersydd" for slik bruk, og jeg ønsket ikke i det hele tatt en repetisjon av tidligere erfaring. Kanskje er det nettopp fordi C# i forbindelse med WinForms er mye nærmere Delphi, som mange av oss en gang startet med. Heldigvis ble den nødvendige løsningen funnet ganske raskt – i form av prosjektet IKVM.NET, som gjør det enkelt å oversette eksisterende Java-programmer til administrert .NET-kode. Riktignok hadde selve prosjektet allerede blitt forlatt av forfatterne på den tiden, men den siste implementeringen tillot meg å utføre de nødvendige handlingene for kildetekstene ganske vellykket. Gorp.

Så jeg gjorde alle nødvendige endringer og satte det hele sammen til en DLL av passende type, som enkelt kunne "hentes" av alle prosjekter for .NET Framework opprettet i Visual Studio. I mellomtiden opprettet jeg et nytt lag for praktisk presentasjon av resultatene som ble returnert Gorp, i form av tilsvarende datastrukturer som vil være praktiske å behandle i en tabellvisning (som tar utgangspunkt i både rader og kolonner; både ordboknøkler og numeriske indekser). Vel, de nødvendige verktøyene for å behandle og vise resultatene ble skrevet ganske raskt.

Prosessen med å tilpasse maler for den nye motoren for å lære den å analysere eksisterende prøver av skannede innholdsfortegnelsestekster forårsaket heller ingen spesielle komplikasjoner. Faktisk trengte jeg ikke engang å referere til mine tidligere maler i det hele tatt: Jeg opprettet ganske enkelt alle nødvendige maler fra bunnen av. Dessuten, hvis malene designet for å fungere med den forrige versjonen av systemet satte et ganske smalt rammeverk for tekster som kunne analyseres korrekt med deres hjelp, har den nye motoren allerede gjort det mulig å utvikle ganske universelle maler som passer for flere typer markeringer på en gang. Jeg prøvde til og med å skrive en slags omfattende mal for en hvilken som helst vilkårlig innholdsfortegnelse, selv om selvfølgelig selv med alle de nye mulighetene som åpnet seg for meg, inkludert spesielt den begrensede muligheten til å implementere de samme nestede repeterende sekvensene ( som for eksempel etternavn og initialer flere forfattere på rad), viste dette seg å være en utopi.

Kanskje i fremtiden vil det være mulig å implementere et bestemt konsept med meta-maler, som vil være i stand til å sjekke kildeteksten for samsvar med flere av de tilgjengelige malene samtidig, og deretter, i samsvar med de oppnådde resultatene, velge mest passende, ved å bruke en slags intelligent algoritme. Men nå var jeg mer bekymret for et annet spørsmål. En parser som Gorp, til tross for all dens allsidighet og modifikasjonene jeg gjorde, var den fortsatt iboende ute av stand til å gjøre en tilsynelatende enkel ting som min selvskrevne parser var i stand til å gjøre fra den aller første versjonen. Nemlig: han hadde evnen til å finne og trekke ut fra kildeteksten alle fragmenter som samsvarer med masken som er spesifisert i malen brukt på riktig sted, samtidig som han overhodet ikke var interessert i hva den gitte teksten inneholder i mellomrommene mellom disse fragmentene. Så langt har jeg bare forbedret den nye motoren litt, slik at den kan søke etter alle mulige nye repetisjoner av en gitt sekvens av slike masker fra den nåværende posisjonen, og etterlater muligheten for tilstedeværelse i teksten av sett med vilkårlige tegn som var fullstendig uten hensyn til i parsingen, innelukket mellom de detekterte repeterende strukturene. Dette gjorde det imidlertid ikke mulig å sette neste maske uavhengig av resultatene av å søke etter det forrige fragmentet ved å bruke den tilsvarende masken: strengheten til den beskrevne tekststrukturen ga fortsatt ikke rom for vilkårlige inkluderinger av uregelmessige tegn.

Og hvis for eksemplene på innholdsfortegnelser som jeg kom over dette problemet ennå ikke virket så alvorlig, så når jeg prøver å bruke en ny parsingmekanisme på en lignende oppgave med å analysere innholdet på et nettsted (dvs. den samme parsingen), begrensninger er her de dukket opp med all sin åpenhet. Tross alt er det ganske enkelt å sette de nødvendige maskene for fragmenter av nettmarkering, mellom hvilke dataene vi leter etter (som må trekkes ut) skal være plassert, men hvordan kan vi tvinge parseren til å umiddelbart gå videre til neste lignende fragment, til tross for alle mulige koder og HTML-attributter som kan plasseres i mellomrommene mellom dem?

Etter å ha tenkt litt, bestemte jeg meg for å introdusere et par tjenestemønstre (%all_before) и (%all_etter), som tjener det åpenbare formålet å sikre at alt som kan være i kildeteksten hoppes over før ethvert mønster (maske) som følger dem. Dessuten, hvis (%all_before) ignorerte rett og slett alle disse vilkårlige inneslutningene (%all_etter)tvert imot, tillot dem å bli lagt til det ønskede fragmentet etter flytting fra forrige fragment. Det høres ganske enkelt ut, men for å implementere dette konseptet måtte jeg kamme gjennom gorp-kildene igjen for å gjøre de nødvendige modifikasjonene for ikke å bryte den allerede implementerte logikken. Til slutt klarte vi å gjøre dette (selv om til og med den aller, aller første, om enn veldig buggy, implementeringen av parseren min ble skrevet, og enda raskere - i løpet av et par uker). Fra nå av fikk systemet en virkelig universell form – ikke mindre enn 12 år etter de første forsøkene på å få det til å fungere.

Selvfølgelig er dette ikke slutten på drømmene våre. Du kan også fullstendig omskrive gorp-malparseren i C#, ved å bruke hvilket som helst av de tilgjengelige bibliotekene for å implementere en gratis grammatikk. Jeg tror koden bør forenkles betydelig, og dette vil tillate oss å bli kvitt arven i form av eksisterende Java-kilder. Men med den eksisterende motortypen er det også fullt mulig å gjøre forskjellige interessante ting, inkludert et forsøk på å implementere meta-malene jeg allerede har nevnt, for ikke å snakke om å analysere forskjellige data fra forskjellige nettsteder (men jeg utelukker ikke at eksisterende spesialiserte programvareverktøy er mer egnet for dette – jeg har bare ikke hatt riktig erfaring med å bruke dem ennå).

Forresten, i sommer mottok jeg allerede en invitasjon på e-post fra et selskap som bruker Salesforce-teknologier (utvikleren av originalen Gorp), bestå et intervju for påfølgende arbeid i Riga. Dessverre er jeg for øyeblikket ikke klar for slike omplasseringer.

Hvis dette materialet vekker en viss interesse, vil jeg i den andre delen forsøke å beskrive mer detaljert teknologien for å kompilere og deretter analysere maler ved å bruke eksempelet på implementeringen brukt i Salesforce Gorp (mine egne tillegg, med unntak av et par funksjonsord som allerede er beskrevet, gjør praktisk talt ingen endringer i selve malsyntaksen, så nesten all dokumentasjon for det originale systemet Gorp Passer også til min versjon).

Kilde: www.habr.com

Legg til en kommentar