Ein Register für’s PDF

Kürzlich stand ich vor der Aufagabe ein Orts- und ein Personenregister für ein PDF zu erzeugen. Der Text lag druckfertig als PDF vor. Das Personen- und Ortsregister als Wörstdokument (natürlich!) – ließ sich aber problemlos als Text speichern, so dass jeweils ein Eintrag pro Zeile ausgegeben wurde – unter Umständen eben länger als eine Bildschirmzeile, weil nicht nur der Name dastand, sondern üppig anreichernde Informationen.

Mit Linuxmitteln ist das eine Aufgabe, die sich nicht vollautomatisch erledigen lässt (nicht dass ich wüßte), aber viele Arbeitsschritte lassen sich teilautomatisieren. Sehr zum Nachteil der armen Textverarbeiter kennen diese natürlich nur selten die Möglichkeiten kleiner Programme wie grep, sed, sort, cat, head, tail und all die anderen. Sonst könnten sie sich das Leben oft selbst vereinfachen.

Als Dokumentation, wie ich vorgegangen bin, auch für mich, liste ich hier die Schritte auf.

Zuerst muss man aus dem Gesamtdokument Buch.pdf eine Serie einzelner PDF-Seiten machen, wofür es ein Programm gibt, was sehr praktisch ist. Um bei 300 Seiten nicht die 300 Dateien im allgemeinen Dateiwust, geschweige dem Desktop zu verteilen verschiebt man dieses vorab am besten in ein frisches Verzeichnis.

Mit pdfseparate
pdfseparate Buch.pdf b-%d.pdf

wandelt man das PDF in lauter einzelne PDF-Seiten um, wobei das %d durch die Seitenzahl ersetzt wird, 1, 2, …, 9, 10 … – nicht 08, 09, 10, …. usw.

Bei mir erzeugt das 270 Dateien. Vorne gibt es ein Vorwort, hinten ein Inhaltsverzeichnis. Ich konvertiere nur die Seiten 8 bis 265 in der Bash mit pdftotext in reinen Ascii-Text oder UTF – wird schon schiefgehen.

for n in {8..265}; do pdftotext b-$n.pdf ; echo -n $n" "; done

Das echo dient dazu, die Geduld nicht zu verlieren, sondern ein Feedback zu bekommen. Es dauert aber weniger als eine Minute auf meinem betagten Blech. Die pdf-Dateien bleiben erhalten wie oben auch das Gesamtdokument – es geht also nichts verloren.

Die Personenliste sieht, exemplarisch, so aus:

Curry, Haskell Curry (12.9.1900 - 1.9.1982) Mathematiker u. Logiker, USA
Kleisli, Heinrich Kleisli, Schweizer Mathematiker
Wadler (Theorems for free) Philip Wadler

Sie war nicht für den Zweck automatischer Suche erzeugt worden und musste daher manuell an einigen Stellen nachbearbeitet werden, so dass der Suchbegriff immer vorne stand, und ein Trenner aus einer knappen Liste diesen abschloß, hier Komma, Leerzeichen oder Klammer auf.

Diese Liste, als Datei pers.txt, wird vom längsten Code des Beitrags zeilenweise gelesen, der Name zu Beginn mit dem Sed-Befehl extrahiert und ausgegeben, und dann ein Befehl ausgegeben, mit dem jeder Name nacheinander auf alle Dateien losgelassen wird, zur Suche:

sed -r 's/^([^,;(]+)[,;(].*/"\1"/' pers.txt | while read name
do
    echo "echo -e \\n\\n${name}"
    muster=$(echo ${name})
    echo egrep -n "${muster}" b-{8..265}.txt
done > such-name.sh

Die Suche wurde nicht sofort ausgeführt, weil sich bei manchen Namen Probleme zeigten, die so am besten in den Griff zu bekommen waren – ich meine bei Namen mit Leerstellen und speziellen Zeichen wie „de Jong“, „Klöve-Großwaldt“ oder „O’Reilly“.

Das Programm produziert also selbst ein Programm, in dem lange Argumentlisten vorkommen, mit 2x Personenzahl Befehlen mit rund Seitenzahl Argumenten.

echo -e \n\n"Curry"
egrep -n "Curry" b-8.txt b-9.txt b-10.txt ... b-264.txt b-265.txt

Dieses Programm muss man also noch laufen lassen. Dabei gibt egrep wegen des Schalters ‚-n‘ die Zeilennummer neben dem Dateinamen aus. Da der Name die Seitenzahl enthält müssen wir nur das „b-“ am Anfang und das „.txt“ abschneiden:

bash such-name.sh | sed -r 's/^nn/\n/;s/^b-([0-9]+).txt:([0-9]+):/\1:\2\t/'

Und erhalten dann eine Liste wie:

Wadler
111:10 Footnote 13mThe idea of free theorems was introduced by Philip Wadler in a classic paper called Theorems

Kleisli
202:18 11.4.2 Kleisli composition
202:26 that are called Kleisli arrows1, and they can be composed with one another:
202:27 Footnote 1mThis name comes from category theory and is after the Swiss mathematician Heinrich Kleisli.
210:23 Kleisli arrow

Curry
29:19 2.7 Currying and partial application
30:9 Footnote 2mThis is named after the mathematician Haskell Curry, who discovered the principle. It was

Die Zeilennummer dient nur als Anhaltspunkt. Je nach Gestaltung der PDF-Seite mit Tabellen, Kopf-Fußzeilen, Grafiken usw. kann die Zeilennummer von einer eigenen Zählweise abweichen, aber um die Stelle zu finden ist sie erstmal hilfreich.

Um die Liste mit den Fundstellen in eine reinen Seitenzahlliste zu verwandeln wird man, nach einer Kontrolle der pers-i.lst und manueller Korrektur, folgende Befehle ausführen:

sed -nr '/^([^0-9]+)/,/^$/{s/^([^0-9]+)/#\1 /p;s/(^[0-9]+).*/\1, /p}' pers-i.lst | tr -d '\n' | tr '#' '\n' | sed 's/, $//'> pers-reg.lst

Das Ergebnis sieht dann so aus:

Wadler 111
Kleisli 202, 202, 202, 210
Curry 29, 30

Dies bedarf noch der Nacharbeit wie des Löschens des Entfernens mehrfacher Seiten (Kleisli: 202). Evtl. will man auch Folgeseiten mit 130f., 140ff. oder 150-156 zusammenfassen. Das wäre evtl. Aufgabe für ein weiteres Skript.

Insgesamt hat das Vorgehen die Schwäche, dass Namen die über Zeilen getrennt sind (Kleis-li) nicht gefunden werden, und Namen, die Teil ordinärer Begriffe sind oder selbst ordinäre Begriffe (Curry => Currywurst, J.S.Bach => Bach als Gewässer) schlecht abzugrenzen sind. Problem 1 scheint mit regulären Ausdrücken fassbar zu sein, in dem man die Wortgrenze mit \W

egrep -n "${muster}\W" ...

Damit handelt man sich aber neue Probleme ein: In Wadlers Text ; hier bekommt der Name eine Endung. Das ist aber mit einem s? für ein optionales s in den Griff zu bekommen:

egrep -n "${muster}s?\W" ...

Für eine vollautomatisierte Listenerstellung taugt das Vorgehen leider nicht, aber um ein Skelett zu erzeugen, allemal. Bei langen Namen, bei denen die Chance steigt, dass sie getrennt werden, kann man evtl. nach der Endung suchen oder dem Beginn, wenn diese/dieser signifikant ist, etwa Sierpinski: Sier kommt selten in anderen Worten vor.

Ungünstiger ist, dass man vorab eine Liste der Namen braucht. Vielleicht, wenn man immer wieder im gleichen Forschungsgebiet arbeitet, hat man bald eine umfangreiche Liste möglicher Kandidaten zusammen, die man mit Kollegen ergänzen kann und immer wieder benutzt, ohne dass es ausufert, weil man Millionen Namen gesammelt hat, und 99% der Namen nicht gefunden werden und ein Großteil der Funde falscher Alarm sind, weil der Name mit einem Gegenstand kollidiert wie Vogel, Schäfer oder Ort.

Ansonsten könnte man auch versuchen alle Großgeschriebenen Wörter die nicht am Satzanfang stehen zu finden, und sie gegen ein Wörterbuch abzugleichen, um Namen zu finden, aber auch das ist wohl relativ fehleranfällig. Falls man aber ohnehin eine Rechtschreibprüfung durchführt könnte es ein Hilfsmittel sein.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.