Nu skal det jo ikke handle om COVID-19, det hele. Sådan tænker jeg i det mindste.
En dag fik min kollega en ordentlig bunke aktindsigt fra Datatilsynet, i forbindelse med Aula-lækket, som Version2 afslørede for nylig.
Der var tale om de blanketter, der skal udfyldes, når en virksomhed eller myndighed ved, at der har fundet et databrud sted, og skal anmelde dette til Datatilsynet.
Dokumenterne er som regel PDF’er i et maskinlæsbart format, hvor teksten kan udtrækkes i en eller anden form.
Masser af tekst
Der er tale om 168 dokumenter. Det vil tage en hulens lang tid at copy-paste de relevante informationer til en form, vi kan bruge til noget. Og det er næppe heller sundt for håndleddet. PDF'er er skidt for os, der skal hive tekst ud.
I første omgang vil vi gerne have en oversigt over
- det enkelte dokuments filnavn,
- myndigheden, der har indsendt aktindsigten,
- dato og tidspunkt,
- og så vil vi gerne have det, der står i punktet ‘Beskriv hændelsen’.
Og gerne det hele på en nem og overskuelig facon, såsom en kommasepareret fil, som man kan åbne i et regnearksprogram.
Problemet er i og for sig ikke så svært. Alle blanketterne er ens, og de felter, vi vil udtrække, er omkranset af samme tekst. Altså lige et job for go’e gamle grep.
Første punkt på dagsordenen er at udtrække teksten fra PDF-filerne. Jeg bruger Bash-shell’en i Ubuntu, under Windows Subsystem for Linux (WSL), som jeg plejer.
Her kan jeg udtrække teksten med Pdftotext-programmet, ved at navigere hen til mappen med dokumenterne og bruge one-liner’en:
for file in *.pdf; do pdftotext "$file" "$file.txt"; done
Nu er der en tekst-udgave af hver af PDF-dokumenterne, med ‘.txt’ i enden af filnavnet, i mappen.
Tekstmønstre trækker tekst ud
Det går jo strygende. Nu skal vi kigge nærmere på tekstfilerne. Vi vil som sagt gerne opsnappe dato og tid. Vi finder tidspunktet i tekstklumpen:
Skema til indberetning af sikkerhedshændelser Identifikation Tidspunkt for indberetningen onsdag maj 15, 2019 13:20:03 Referencenummer a93068d77aca5b0ae2991b8af07021ed6f93da6e Anmelderinformation
Vores tekstmønster i regular expressions - programmeringsverdens universelle sprog til mønstergenkendelse i tekst - skal altså være noget i stil med
Tidspunkt for indberetningen(.*)Referencenummer
Punktum betyder ‘alle tegn’, stjerne betyder ‘nul eller flere gange’, og parentesen angiver en ‘capture group’, som er den tekst, vi vil indfange.
Grep kan ikke finde ud af kun at returnere capture group’en, så jeg bruger alternativet Pcregrep - ‘a grep with Perl-compatible regular expressions,’ som der står på manual-siden.
Det betyder, at jeg kan bruge den helt fantastiske og gratis webapp Regex101.com til på interaktiv vis at forfine mit søgemønster, så den gør det, jeg vil have. Efter lidt nørkleri når jeg frem til dette:
pcregrep -M -o1 "(?s)Tidspunkt for indberetningen(.*?)Referencenummer" 2019-442-4960.pdf.txt
Flaget M står for ‘søg i hele teksten og ikke blot enkelte linjer’, o1 betyder ‘udskriv første capture group’. Det mystiske ‘?s’ i starten af selve søgestregen siger, at punktum-symbolet, som altså betyder 'alle tegn', også matcher linjeskift. Vores match kan altså løbe over linjeskift.
Vores capture group ser nu sådan ud: (.*?)
og her betyder spørgsmålstegnet, at gruppen ikke skal være ‘grådig.’
Det kræver en lille forklaring. I nogle af PDF’erne er der mere end én blanket, fordi myndighederne har indsendt flere anmeldelser til Datatilsynet og samlet dem i ét PDF-dokument.
Hvis spørgsmålstegnet udelades, finder mønsteret den størst mulige streng, og det er så den, der går fra starten af mønsteret i første dokument og frem til slutningen af mønsteret i det andet dokument. Med spørgsmålstegnet finder vi kun lige det, vi vil have - nemlig dato og tid.
Øv, mit dumme program
Ak! Det er ikke lige så nemt. Jeg bruger en tåbelig mængde tid på at forfine min kommando, som nu er gået hen og blevet til et helt lille script i Bash:
echo "" > ../ud2.txt for file in \*; do echo $file >> ../ud2.txt pcregrep -M -o1 -e "(?s)Tidspunkt for indberetningen(.\*?)Referencenummer" "$file" | sed -e 's/^[[:space:]]\*//' >> ../ud2.txt echo -e "," >> ../ud2.csv pcregrep -M -o1 -e "(?s)virksomhedens navn(.\*?)Afdeling" "$file" | sed -e 's/^[[:space:]]\*//' >> ../ud2.txt done
(For enkelthedens skyld er det her kun de to første felter i filerne, som vi udtrækker.)
Men resultatet er slet ikke så raffineret som det, jeg gerne vil have. Jeg kan ikke se, hvordan jeg kommer fra et tekstfil, der har felterne med, og over i et kommasepareret format.
Det kan dog sagtens lade sig gøre, for bash-scripting og sed er regulære programmeringssprog. Men det bliver næppe enkelt eller elegant.
Jeg må smide håndklædet i ringen og se i øjnene, at det for mig er hurtigere at skrive et program i et sprog, jeg kender.
Men hvorfor ikke gøre en dyd af nødvendigheden og skrive et værktøj, der på simpel vis kan udtrække tekst og pløje det lige ind i et csv-dokument? Det er der nok andre, der kunne få brug for, såsom journalister og andet godtfolk.
Ligesom i eventyret om konen med æggene svimler jeg hen, og forestiller mig en dag, hvor man blot skal skrive
extract2csv
og så
Command 'extract2csv' not found, but can be installed with: sudo apt install extract2csv
Derefter følger berømmelsen og pengene, naturligvis. I hvert fald i fantasien.
Nemme tekstmønstre
Men fantasi eller ej, programmet skal skrives først. Som udgangspunkt forestiller jeg mig, at teksten er udtrukket fra kildedokumenterne med pdftotext, eller docx2txt eller andre programmer der kan udtrække tekst fra forskellige slags formater.
Jeg vender tilbage til webappen Regex101.com, som jeg altså bruger til at udvikle mit søgemønster. Webappen har en rigtig smart funktion, som skriver søgemønsteret om til kode i Perl, Javascript og mere til, som man lige kan kopiere ind i sit program. Jeg bruger Java, og kan godt huske, at jeg syntes at api’et til regular expressions kan være lidt kringlet. Her klikker jeg bare på en knap, og så er mit program halvt færdigt.
Det næste punkt går ud på at skrive den fundne tekst i et kommasepareret dokument. Det gjorde vi i en Version2-artikel for et par år siden, og det var ganske nemt.
Så det springer vi over her.
Jeg tænker mig at den nemmeste syntaks for mønstre er noget i stil med:
extract2csv "Tidspunkt for indberetningen\*Referencenummer" "virksomhedens navn\*Afdeling" "Beskriv hændelsen\*Hvor fandt hændelsen fysisk sted"
Bum - så er der en kommasepareret fil med de rigtige felter, med navnet out.csv, i mappen.
Sådan en simpel kommando kan journalister vel godt finde ud af - i hvert fald sådan nogen af slagsen, vi har her på Version2 og Ingeniøren.
Inde i mit lille program udskifter jeg blot stjernen i det forsimplede søgemønster med regexp-udtrykket (.*?)
som vi så tidligere.
Koden er lagt på Github, og der er plads til forbedringer. Jeg har ikke nået at skrive en eneste unit-test, og jeg kan allerede få øje på en kæmpe fejl.
Det ville også være smart med lidt flere muligheder, så som at brugeren selv kan vælge ud-filen, med mere. Og det ville være godt at producere noget nemmere eksekverbart, så som en jar-fil, eller måske compile til maskinkode?
Og hvad med Githubs indbyggede continuous integration, det kan vel også noget? Vi kigger nærmer på mulighederne i næste artikel, så bliv hængende på kanalen.

...men det er dyrt at lave god journalistik. Derfor beder vi dig overveje at tegne abonnement på Version2.
Digitaliseringen buldrer derudaf, og it-folkene tegner fremtidens Danmark. Derfor er det vigtigere end nogensinde med et kvalificeret bud på, hvordan it bedst kan være med til at udvikle det danske samfund og erhvervsliv.
Og der har aldrig været mere akut brug for en kritisk vagthund, der råber op, når der tages forkerte it-beslutninger.
Den rolle har Version2 indtaget siden 2006 - og det bliver vi ved med.