Alles über die Verwendung von regulären Ausdrücken in Calibre

Regular expressions are features used in many places in calibre to perform sophisticated manipulation of e-book content and metadata. This tutorial is a gentle introduction to getting you started with using regular expressions in calibre.

Vorweg ein Wort der Warnung - und der Ermutigung

Nun wird es unvermeidbar ein wenig technisch - immerhin sind reguläre Ausdrücke ein technisches Werkzeug, mit dem man technisches Zeug erledigt. Ich werde den einen oder anderen Fachbegriff verwenden und Gedankengänge, die kompliziert oder auch verwickelt wirken mögen. Ich werde versuchen, diese Gedanken so gut ich kann zu erklären, aber ich werde nicht ohne sie auskommen. Nur sollten Sie sich von diesem Fachchinesisch nicht abschrecken lassen, denn ich habe versucht, alle neuen Begriffe zu erklären. Und obwohl reguläre Ausdrücke an sich wie mysteriöse, schwarze Magie wirken (oder, nüchterner gesagt, wie ein Durcheinander irgendwelcher Buchstaben, Zahlen und Zeichen), verspreche ich hiermit, dass sie gar nicht so kompliziert sind. Selbst diejenigen, die reguläre Ausdrücke wirklich gut verstehen, haben schon mal Probleme, die komplexeren zu durchschauen, aber sie aufzuschreiben ist gar nicht so schwer - Sie bauen den Ausdruck schrittweise zusammen. Also: Treten Sie vor und folgen Sie mir in den Kaninchenbau!

Wo in calibre sind reguläre Ausdrücke verwendbar?

There are a few places calibre uses regular expressions. There’s the Search & replace in conversion options, metadata detection from filenames in the import settings and Search & replace when editing the metadata of books in bulk. The calibre book editor can also use regular expressions in its search and replace feature.

Was um Himmels Willen ist ein „regulärer Ausdruck“?

Reguläre Ausdrücke beschreiben Mengen von Zeichen oder Zeichenfolgen. Ein einzelner regulärer Ausdruck entspricht unter Umständen einer ganzen Reihe verschiedener Zeichenfolgen. Und das macht reguläre Ausdrücke so mächtig: Sie beschreiben kurz und knapp eine möglicherweise große Zahl von Varianten.

Bemerkung

I’m using string here in the sense it is used in programming languages: a string of one or more characters, characters including actual characters, numbers, punctuation and so-called whitespace (linebreaks, tabulators etc.). Please note that generally, uppercase and lowercase characters are not considered the same, thus „a“ being a different character from „A“ and so forth. In calibre, regular expressions are case insensitive in the Search bar, but not in the conversion options. There’s a way to make every regular expression case insensitive, but we’ll discuss that later. It gets complicated because regular expressions allow for variations in the strings it matches, so one expression can match multiple strings, which is why people bother using them at all. More on that in a bit.

Bitte etwas genauer!

Klar, darum machen wir das ja! Der wichtigste Gedanke bei regulären Ausdrücken zuerst: Eine Zeichenfolge an sich ist ein regulärer Ausdruck, der sich selbst entspricht. Wenn ich also einen regulären Ausdruck für die Zeichenfolge \"Hallo, Welt!" suche, kann ich einfach "Hallo, Welt!" benutzen. Ja, es wirklich so simpel! Sie sollten beachten, dass dies nur für die exakte Zeichenfolge "Hallo, Welt!" gilt, nicht etwa für "Hallo, wElt!" oder "hallo, welt!" oder ähnliche Zeichenfolgen.

Das hört sich nicht schlecht an. Was kommt als Nächstes?

Next is the beginning of the really good stuff. Remember where I said that regular expressions can match multiple strings? This is where it gets a little more complicated. Say, as a somewhat more practical exercise, the e-book you wanted to convert had a nasty footer counting the pages, like „Page 5 of 423“. Obviously the page number would rise from 1 to 423, thus you’d have to match 423 different strings, right? Wrong, actually: regular expressions allow you to define sets of characters that are matched: To define a set, you put all the characters you want to be in the set into square brackets. So, for example, the set [abc] would match either the character „a“, „b“ or „c“. Sets will always only match one of the characters in the set. They „understand“ character ranges, that is, if you wanted to match all the lower case characters, you’d use the set [a-z] for lower- and uppercase characters you’d use [a-zA-Z] and so on. Got the idea? So, obviously, using the expression Page [0-9] of 423 you’d be able to match the first 9 pages, thus reducing the expressions needed to three: The second expression Page [0-9][0-9] of 423 would match all two-digit page numbers, and I’m sure you can guess what the third expression would look like. Yes, go ahead. Write it down.

Hey, toll! Langsam bekommt alles einen Sinn!

Oh, ich hatte gehofft, dass Sie das sagen. Aber seien Sie bereit, jetzt wird es noch besser! Bisher haben wir gesehen, dass Mengen einem von mehreren Zeichen entsprechen. Aber man kann ein Zeichen oder eine Menge sogar wiederholen, und dadurch brauchen wir für das Beispiel mit den Seitenzahlen nur noch einen Ausdruck. Genau, EINEN! Aufregend, was? Ist es wirklich! Es funktioniert so: Einige sogenannte Sonderzeichen, nämlich „+“, „?“ und „*“, wiederholen das Einzelelement direkt davor. (Element bedeutet hier entweder ein einzelnes Zeichen, eine Zeichenmenge, eine „Escape-Sequenz“, eine „Gruppe“ - mehr dazu später - oder kurz: jede beliebige Einheit eines regulären Ausdrucks.) Diese Zeichen werden als „Wildcards“, also als Joker, oder auch als Quantifizierer bezeichnet. Genauer gesagt, bedeutet das Fragezeichen „?“ 0 oder 1 Mal das vorherige Element, der Stern „*“ 0 oder mehrere Male das vorherige Element und das Pluszeichen „“+“ bedeutet 1 oder mehrere Male das vorherige Element. Ein paar Beispiele: Der Ausdruck a? würde „“ (also der leeren Zeichenfolge, was hier nicht gerade brauchbar wäre) oder „a“ entsprechen. Der Ausdruck a* würde „“, „a“, „aa“ oder noch mehr aufeinanderfolgenden a’s entsprechen. Und a+ entspricht „a“, „aa“ oder noch mehr aufeinanderfolgenden a’s - aber eben nicht der leeren Zeichenfolge! Dasselbe gilt für Mengen: Der Ausdruck [0-9]+ entspricht jeder natürlichen Zahl, egal welcher Länge! Jetzt weiß ich, was Sie denken: Wäre das nicht genau richtig für das Beispiel oben mit den Seitenzahlen? Ja, da haben Sie Recht: Der Ausdruck Seite [0-9]+ von 423 ganz für sich alleine würde allen Seitenangaben in diesem Buch entsprechen!

Bemerkung

Einen Hinweis zu diesen Jokern hätte ich aber: Sie haben die unschöne Angewohnheit, grundsätzlich so viel Text wie möglich entsprechen zu wollen. Seien Sie also vorsichtig im Umgang mit ihnen. Man bezeichnet dieses Verhalten auch als gierig oder gefräßig - Sie verstehen sicher, warum. Probleme machen zum Beispiel Markierungszeichen („Tags“), wie in der Zeichenfolge "<p class="calibre2">Titel fehlt hier</p>". Sagen wir, Sie möchten nach dem öffnenden „Tag“ (zwischen den ersten beiden spitzen Klammern - mehr zu „Tags“ später) suchen und halten den Ausdruck <p.*> für passend. Tatsächlich aber entspricht Ihr Ausdruck aber schon der gesamten Zeichenfolge! (Ach so: Das Zeichen „.“ ist auch ein Sonderzeichen. Es entspricht praktisch allem außer einem Zeilenumbruch, so daß grundsätzlich der Ausdruck .* auf jede vorstellbare Textzeile passt.) Benutzen Sie also stattdessen <p.*?>, wodurch der Joker "*" seine Gefräßigkeit verliert. Dieser Ausdruck erfasst wirklich nur den ersten, öffnenden „Tag“, wie geplant. Genau genommen, gibt es noch eine andere Möglichkeit: Der Ausdruck <p[^>]*> würde ebenfalls nur dem öffnenden „Tag“ entsprechen. Eine Begründung folgt später. Hier nur so viel: Recht häufig gibt es mehr als eine Art, einen regulären Ausdruck z formulieren.

Tja, das mit den Sonderzeichen ist ja gut und schön. Aber wie kann ich nach einem Punkt oder einem Fragezeichen suchen?

Natürlich geht auch so etwas: Sie brauchen nur einen „Backslash“ vor ein Sonderzeichen zu setzen, und es wird als das Zeichen selbst verstanden, ohne irgendeine Sonderbedeutung. Eine solche Kombination aus einem „Backslash“ mit einem einzelnen Zeichen nennt man Escape-Sequenz, und unter „Escaping“ versteht man, vor ein Sonderzeichen einen „Backslash“ zu schreiben. Solche Escape-Sequenzen gelten als einzelne Elemente. Es gibt auch Escape-Sequenzen, die nicht nur Sonderzeichen betreffen; zum Beispiel ist "\t" die Sequenz für einen Tabulatorschritt. Wir kommen später noch zu ähnlichen Escape-Sequenzen. Was die Sonderzeichen betrifft, sollten Sie einfach mal folgendes annehmen: Jedes Zeichen in dieser Einführung, das eine besondere Funktion hat, gilt als Sonderzeichen und benötigt den „Backslash“, wenn man das Zeichen selbst meint.

So, und welche Mengen sind nun am brauchbarsten?

Ich wusste, du würdest fragen. Nützliche Formulierungen sind [0-9] entspricht einer einzelnen Zahl, [a-z] entspricht einem einzelnen Kleinbuchstaben, [A-Z] entspricht einem einzelnen Grossbuchstaben, [a-zA-Z] entspricht einem einzelnen Buchstaben und [a-zA-Z0-9] entspricht einem einzelnen Buchstabe oder Zahl. Du kannst auch eine „Escape-Sequenz“ als Abkürzung benutzen:

\d is equivalent to [0-9]
\w is equivalent to [a-zA-Z0-9_]
\s is equivalent to any whitespace

Bemerkung

Als „Leerraum“ bezeichnen wir alles, was beim Druck nicht sichtbar ist. Das sind zum Beispiel Leerzeichen, Tabulatoren, Zeilen- und Seitenvorschübe.

Ein letzter Hinweis zu Mengen: Sie können eine Menge auch so festlegen, dass jedes Zeichen gemeint ist außer denen in der Menge! Dazu schreiben Sie als allererstes Zeichen ein "^" in die Menge. So entspricht [^a] jedem Zeichen außer dem „a“. Man spricht auch vom Komplement dieser Menge. Die Abkürzungen mit Hilfe von Escape-Sequenzen funktionieren auch mit Komplementbildung: "\D" bedeutet so viel wie „alles außer Zahlen“, anders ausgedrückt also [^0-9]. Der oben erwähnte Ausdruck <p[^>]*> versucht, wie man sieht, jedem Zeichen zu entsprechen mit Ausnahme der schließenden spitzen Klammer.

Aber wenn ich jetzt nach ein paar unterschiedlichen Zeichenfolgen gleichzeitig suchen möchte - wird die Sache dann sehr kompliziert?

Fear not, life still is good and easy. Consider this example: The book you’re converting has „Title“ written on every odd page and „Author“ written on every even page. Looks great in print, right? But in e-books, it’s annoying. You can group whole expressions in normal parentheses, and the character "|" will let you match either the expression to its right or the one to its left. Combine those and you’re done. Too fast for you? Okay, first off, we group the expressions for odd and even pages, thus getting (Title)(Author) as our two needed expressions. Now we make things simpler by using the vertical bar ("|" is called the vertical bar character): If you use the expression (Title|Author) you’ll either get a match for „Title“ (on the odd pages) or you’d match „Author“ (on the even pages). Well, wasn’t that easy?

Sie können den senkrechten Strich natürlich auch ohne gruppierende Klammern verwenden. Erinnern Sie sich, dass ich sagte Quantifizierer wiederholten das vorangegangene Element? Nun, der senkrechte Strich funktioniert ein wenig anders: Der Ausdruck „Titel|Autor“ wird also entweder die Zeichenfolge „Titel“ oder die Zeichenfolge „Autor“ finden, genau wie das vorhergehende Beispiel mit der Gruppierung. Der senkrechte Strich wählt aus zwischen der ganzen Zeichenfolge vor und der nach ihm. Wollten Sie also die Zeichenfolgen „Calibre“ und „calibre“ finden und dabei nur zwischen dem großen und dem kleinen „c“ wählen, müssten Sie den Ausdruck (c|C)alibre verwenden, bei dem die Gruppierung sicherstellt, dass nur das „c“ gewählt wird. Sollten Sie c|Calibre verwenden, fänden Sie die Zeichenfolgen „c“ oder „Calibre“, was nicht unsere Absicht war. Kurz gesagt: Verwenden Sie im Zweifel die Gruppierung zusammen mit dem senkrechten Strich.

Da fehlt noch …

… es gäbe da noch eine letzte, wirklich geschickte Sache, die Sie mit Gruppen anstellen können. Wenn Sie einen Gruppierungsausdruck haben, den Sie zuvor eingesetzt haben, können Sie Referenzen auf diese Gruppe im weiteren Ausdruck verwenden: Gruppen werden beginnend mit 1 nummeriert, und Sie werden referenziert, indem die Nummer der Gruppe mit einer „Escape-Sequenz“ versehen wird, folglich würde die fünfte Gruppe als \5 referenziert. Daher fänden Sie bei einer Suche nach ([^ ]+) \1 in der Zeichenfolge „Test Test“ die komplette Zeichenfolge!

Hatten Sie anfangs nicht gesagt, dass reguläre Ausdrücke auch unabhängig von Groß- und Kleinschreibung gemacht werden könnten?

Yes, I did, thanks for paying attention and reminding me. You can tell calibre how you want certain things handled by using something called flags. You include flags in your expression by using the special construct (?flags go here) where, obviously, you’d replace „flags go here“ with the specific flags you want. For ignoring case, the flag is i, thus you include (?i) in your expression. Thus, (?i)test would match „Test“, „tEst“, „TEst“ and any case variation you could think of.

Another useful flag lets the dot match any character at all, including the newline, the flag s. If you want to use multiple flags in an expression, just put them in the same statement: (?is) would ignore case and make the dot match all. It doesn’t matter which flag you state first, (?si) would be equivalent to the above.

Mir scheint, ich verstehe allmählich etwas von regulären Ausdrücken … und wie benutze ich sie nun in calibre?

Konvertierungen

Let’s begin with the conversion settings, which is really neat. In the Search & replace part, you can input a regexp (short for regular expression) that describes the string that will be replaced during the conversion. The neat part is the wizard. Click on the wizard staff and you get a preview of what calibre „sees“ during the conversion process. Scroll down to the string you want to remove, select and copy it, paste it into the regexp field on top of the window. If there are variable parts, like page numbers or so, use sets and quantifiers to cover those, and while you’re at it, remember to escape special characters, if there are some. Hit the button labeled Test and calibre highlights the parts it would replace were you to use the regexp. Once you’re satisfied, hit OK and convert. Be careful if your conversion source has tags like this example:

Maybe, but the cops feel like you do, Anita. What's one more dead vampire?
New laws don't change that. </p>
<p class="calibre4"> <b class="calibre2">Generated by ABC Amber LIT Conv
<a href="http://www.processtext.com/abclit.html" class="calibre3">erter,
http://www.processtext.com/abclit.html</a></b></p>
<p class="calibre4"> It had only been two years since Addison v. Clark.
The court case gave us a revised version of what life was

(shamelessly ripped out of this thread). You’d have to remove some of the tags as well. In this example, I’d recommend beginning with the tag <b class="calibre2">, now you have to end with the corresponding closing tag (opening tags are <tag>, closing tags are </tag>), which is simply the next </b> in this case. (Refer to a good HTML manual or ask in the forum if you are unclear on this point.) The opening tag can be described using <b.*?>, the closing tag using </b>, thus we could remove everything between those tags using <b.*?>.*?</b>. But using this expression would be a bad idea, because it removes everything enclosed by <b>- tags (which, by the way, render the enclosed text in bold print), and it’s a fair bet that we’ll remove portions of the book in this way. Instead, include the beginning of the enclosed string as well, making the regular expression <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b> The \s with quantifiers are included here instead of explicitly using the spaces as seen in the string to catch any variations of the string that might occur. Remember to check what calibre will remove to make sure you don’t remove any portions you want to keep if you test a new expression. If you only check one occurrence, you might miss a mismatch somewhere else in the text. Also note that should you accidentally remove more or fewer tags than you actually wanted to, calibre tries to repair the damaged code after doing the removal.

Hinzufügen von Büchern

Etwas anderes, wofür Sie reguläre Ausdrücke verwenden können, ist das Extrahieren von Metadaten aus Dateinamen. Sie finden diese Funktion im Bereich „Bücher hinzufügen“ der Einstellungen.Es gibt hierbei eine Spezialfunktion: Sie können Feldnamen für die Metadaten-Felder vewenden, beispielsweise bedeutete (?P<title>), dass Calibre diesen Teil der Zeichenfolge als Buchtitel einsetzen würde. Die erlaubten Feldnamen sind in den Fenstern aufgelistet, zusammen mit einem weiteren netten Testfeld. Ein Beispiel: Nehmen wir an, Sie wollen einen ganzen Haufen Dateien importieren, die ähnlich wie Klassische Texte: Die göttliche Komödie von Dante Alighieri.mobi benannt sind. (Selbstverständlich befindet sich das schon in Ihrer Bibliothek, denn wir alle lieben klassische italienische Dichtkunst.) … oder Monumentalwerke der Science-Fiction: Die Foundation-Trilogie von Isaac Asimov.epub. Dies ist offensichtlich ein Namensschema, aus dem Calibre keinerlei sinnvolle Informationen extrahieren wird – sein Standardausdruck für die Extraktion von Metadaten ist (?P<title>.+) - (?P<author>[^_]+). Ein regulärer Ausdruck, der hier funktionieren würde, wäre [a-zA-Z]+: (?P<title>.+) von (?P<author>.+). Beachten Sie bitte, dass Sie in der Gruppe für das jeweilige Metadaten-Feld Ausdrücke einsetzen müssen, um zu beschreiben was eigentlich auf das Feld passt. Und beachten Sie außerdem, dass Sie beim verwenden von Calibres vorgegebenem Testfeld die Dateiendung zu ihrem Versuchs-Dateinamen hinzufügen müssen, andernfalls werden Sie trotz eines korrekten Ausdrucks nichts finden.

Gleichzeitiges Bearbeiten von Metadaten

Der letzte Teil ist das Suchen und Ersetzen in Metadaten-Feldern mithilfe von regulären Ausrücken. Sie können darauf zugreifen, indem Sie mehrere Bücher in der Bibliothek auswählen und Metadaten-Stapelverarbeitung darauf anwenden.Seien Sie sehr vorsichtig beim Verwenden dieser letzten Funktion, denn sie kann ihrer Bibliothek schlimme Schäden zufügen! Überprüfen Sie zweimal, ob Ihre Ausdrücke das tun, was sie sollen, indem Sie die Testfelder verwenden, und wählen Sie nur die Bücher aus, die Sie auch wirklich verändern wollen! Im Suchmodus für reguläre Ausdrücke können Sie in einem Feld suchen, den Text mit etwas anderem ersetzen und sogar das Suchergebnis in ein anderes Feld übertragen. Ein praktisches Beispiel: Nehmen wir an Ihre Bibliothek enthielte die Bücher aus Frank Herberts Dune-Serie, benannt nach dem Schema Dune 1 - Der Wüstenplanet, Dune 2 - Der Herr des Wüstenplaneten und so weiter. Nun wollen Sie Dune in das Serien-Feld bekommen. Das erreichen Sie, indem Sie im Titel-Feld nach (.*?) \d+ - .* suchen und das Serien-Feld mit \1 ersetzen. Sehen Sie was ich da gemacht habe? Das ist eine Referenz zur ersten Gruppe, mit der Sie so das Serien-Feld ersetzen. Jetzt, da die Serie abgehakt ist, müssen Sie nur noch eine weitere Suche nach .*? - im Titel-Feld durchführen und es mit "" (einer leeren Zeichenfolge), wieder im Titel-Feld, ersetzen und ihre Metadaten sind sauber und ordentlich. Ist das nicht großartig? Übrigens können Sie auch an ein Feld vorne oder hinten anfügen, anstatt das Ganze zu ersetzen. Sie können also, wenn Sie möchten, dass der Titel mit der Serien-Info beginnt, auch das erreichen. Wie Sie inzwischen zweifellos bemerkt haben, gibt es da ein Kontrollkästchen mit der Beschriftung Groß-/Kleinschreibung, also brauchen Sie in diesem Fall keine Flags einzusetzen, um das Verhalten zu steuern.

So, damit ist die sehr kurze Einführung in reguläre Ausdrücke beendet. Hoffentlich habe ich Ihnen so viel gezeigt, dass Sie erstmal einen Anfang finden und dann weiter auf eigene Faust lernen können. Ein guter Ausgangspunkt dafür wäre die (englischsprachige) `Python documentation for regexps " "<https://docs.python.org/2/library/re.html>`_.

Noch eine letzte Warnung: Reguläre Ausdrücke sind mächtig, können aber wirklich leicht Unheil anrichten. Darum gibt es calibre hervorragende Möglichkeiten festzustellen, ob Ihre regulären Ausdrücke genau das tun, was sie sollen. Nutzen Sie das aus! Versuchen Sie nicht, sich selbst ein Bein zu stellen. (Hach, diesen Ausdruck liebe ich …) Wenn Sie aber trotz dieser Warnung über Ihre Beine (oder andere Körperteile) stolpern, dann nehmen Sie es als eine Lehre.

Danksagung

Ich bedanke mich für Hinweise, Korrekturen und so weiter bei:

  • ldolse
  • kovidgoyal
  • chaley
  • dwanthny
  • kacir
  • Starson17
  • Orpheu

Mehr über Reguläre Ausdrücke können Sie im The Python User Manual lesen.