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

Reguläre Ausdrücke werden an vielen Stellen in calibre verwendet, um den Inhalt und die Metadaten von E-Büchern auf raffinierte Weise zu manipulieren. Dieses Tutorial ist eine vorsichtige Einführung, mit der man den Umgang mit regulären Ausdrücken erlernen kann.

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?

Es gibt nur wenige Stellen, an denen Calibre Reguläre Ausdrücke verwendet. Da wären, die Search & replace in den Konvertierungsoptionen, Metadatenerkennung auf Grund von Dateinamen in the Importeinstellungen und Suchen & Ersetzen beim massenhaften Bearbeiten der Metadaten von Büchern. Der Calibre-Bucheditor kann ebenfalls Reguläre Ausdrücke in seiner Search and replace-Funktion verwenden. Schließlich kannst du Reguläre Ausdrücke auch beim Suchen in der Calibre-Buchliste und innerhalb des Calibre-E-Book-Betrachters verwenden.

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

Dabei benutze ich den Begriff Zeichenfolge so wie in einer Programmiersprache, nämlich eine Folge von einem oder mehreren Zeichen, wobei als Zeichen die eigentlichen Buchstaben und Zahlen, aber auch Satzzeichen und sogenannten Leerraum (Umbrüche, Tabulatorschritte usw.) gelten. Man beachte, dass meistens Klein- und Großbuchstaben nicht als gleichwertig angesehen werden. Es ist also nicht egal, ob man „a“ oder „A“ schreibt. In der Suchleiste von calibre spielt die Groß- oder Kleinschreibung keine Rolle, wohl aber bei den Einstellungen für die Konvertierung. Es gibt zwar eine Möglichkeit, alle regulären Ausdrücke unabhängig von Klein- und Großbuchstaben zu machen, aber dazu kommen wir später. Es wird kompliziert, weil reguläre Ausdrücke Abweichungen in den Zeichenfolgen zulassen, denen sie entsprechen. Anders gesagt: Ein einziger passt zu verschiedenen Zeichenfolgen - deshalb werden reguläre Ausdrücke ja überhaupt verwendet. Mehr dazu in Kürze.

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?

Als Nächstes kommen jetzt die richtig guten Sachen. Sie erinnern sich daran, dass reguläre Ausdrücke zu verschiedenen Zeichenfolgen passen können? Da wird es nun etwas komplizierter. Mal angenommen, als praktisches Beispiel, Sie wollen ein Buch konvertieren mit einer ganz hässlichen Zeilenangabe in der Fußzeile, so etwas wie „Seite 5 von 423“. Offensichtlich steigt die Seitenzahl von 1 bis 423, und Sie bräuchten 423 verschiedene Zeichenfolgen, oder? Falsch: Tatsächlich können Sie mit regulären Ausdrücken Zeichenmengen festlegen, die zueinander passen. Zur Festlegung einer solchen Menge schreiben Sie alle betreffenden Zeichen in eckige Klammern. Zum Beispiel entspricht die Menge [abc] entweder dem Zeichen „a“, „b“ oder „c“. Mengen entsprechen immer nur einem der Zeichen dieser Menge. Sie „verstehen“ übrigens auch Zeichen-Bereiche, so dass Sie für alle Kleinbuchstaben (ohne Umlaute) die Menge [a-z] und für sämtliche Klein- und Großbuchstaben (wieder ohne Umlaute) die Menge [a-zA-Z] schreiben können - und so weiter. Soweit klar? Mit dem regulären Ausdruck Seite [0-9] von 423 hätten Sie also etwas Passendes für die ersten neun Seiten und benötigten nun nur noch drei Ausdrücke: Seite [0-9][0-9] von 423 wäre der zweite Ausdruck, entsprechend den zweistelligen Seitenzahlen, und den dritten Ausdruck können Sie sicherlich selbst finden. Ja, versuchen Sie es und schreiben Sie ihn auf!

Hey, toll! Langsam bekommt alles einen Sinn!

I was hoping you’d say that. But brace yourself, now it gets even better! We just saw that using sets, we could match one of several characters at once. But you can even repeat a character or set, reducing the number of expressions needed to handle the above page number example to one. Yes, ONE! Excited? You should be! It works like this: Some so-called special characters, „+“, „?“ and „*“, repeat the single element preceding them. (Element means either a single character, a character set, an escape sequence or a group (we’ll learn about those last two later)- in short, any single entity in a regular expression). These characters are called wildcards or quantifiers. To be more precise, „?“ matches 0 or 1 of the preceding element, „*“ matches 0 or more of the preceding element and „+“ matches 1 or more of the preceding element. A few examples: The expression a? would match either „“ (which is the empty string, not strictly useful in this case) or „a“, the expression a* would match „“, „a“, „aa“ or any number of a’s in a row, and, finally, the expression a+ would match „a“, „aa“ or any number of a’s in a row (Note: it wouldn’t match the empty string!). Same deal for sets: The expression [0-9]+ would match every integer number there is! I know what you’re thinking, and you’re right: If you use that in the above case of matching page numbers, wouldn’t that be the single one expression to match all the page numbers? Yes, the expression Page [0-9]+ of 423 would match every page number in that book!

Bemerkung

A note on these quantifiers: They generally try to match as much text as possible, so be careful when using them. This is called „greedy behaviour“- I’m sure you get why. It gets problematic when you, say, try to match a tag. Consider, for example, the string "<p class="calibre2">Title here</p>" and let’s say you’d want to match the opening tag (the part between the first pair of angle brackets, a little more on tags later). You’d think that the expression <p.*> would match that tag, but actually, it matches the whole string! (The character „.“ is another special character. It matches anything except linebreaks, so, basically, the expression .* would match any single line you can think of). Instead, try using <p.*?> which makes the quantifier "*" non-greedy. That expression would only match the first opening tag, as intended. There’s actually another way to accomplish this: The expression <p[^>]*> will match that same opening tag- you’ll see why after the next section. Just note that there quite frequently is more than one way to write a regular expression.

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?

Knew you’d ask. Some useful sets are [0-9] matching a single number, [a-z] matching a single lowercase letter, [A-Z] matching a single uppercase letter, [a-zA-Z] matching a single letter and [a-zA-Z0-9] matching a single letter or number. You can also use an escape sequence as shorthand:

\d

is equivalent to [0-9]

\w

is equivalent to [a-zA-Z0-9_]

\s

is equivalent to any whitespace

Bemerkung

„Whitespace“ is a term for anything that won’t be printed. These characters include space, tabulator, line feed, form feed, carriage return, non-breaking spaces, etc.

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?

Keine Angst, das Leben ist noch immer schön und leicht. Betrachten Sie dieses Beispiel: Das Buch, das Sie konvertieren hat „Titel“ auf jeder ungeraden, und „Autor“ auf jeder geraden Seite stehen. Das sieht im Druck toll aus, stimmt’s? Nur: Bei Ebooks ist das lästig. Sie können ganze Ausdrücke mit normalen Klammern gruppieren, und das Zeichen "|" erlaubt Ihnen entweder den Ausdruck links oder denjenigen rechts davon zu finden. Kombinieren Sie diese beiden und sie haben es. Das war zu schnell? OK, als Erstes kombinieren wir die Ausdrücke für gerade und ungerade Seiten, erhalten also (Title)(Author) für unsere zwei benötigten Ausdrücke. Nun vereinfachen wir das Ganze, indem wir den senkrechten Strich verwenden ("|" wird senkrechter Strich oder Verkettungszeichen genannt): Wenn Sie den Ausdruck (Title|Author) verwenden finden Sie entweder „Titel“ (auf den ungeraden Seiten) oder „Autor“ (auf den geraden Seiten). Na, war das nicht einfach?

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?

Ja, das habe ich. Danke für’s Aufpassen. Sie können Calibre sagen, wie es mit bestimmten Dingen umgehen soll, indem Sie sogenannte „Flags“ verwenden. Sie fügen Flags in ihren Ausdruck mithilfe des speziellen Konstrukts (?Flag hierher) ein, in welchem Sie selbstverständlich „Flags hierher“ durch die genauen Flags, die Sie wollen, ersetzen würden. Um die Groß-/Kleinschreibung zu ignorieren lautet die Flag i, folglich würden Sie (?i) in Ihren Ausdruck einfügen. Also würde (?i)test mit „Test“, „tEst“, „TEst“ übereinstimmen und jegliche Variation der Groß-/Kleinschreibung, die Ihnen einfällt.

Das Flag s sorgt dafür, dass mit dem Punkt jedes beliebige Zeichen gefunden wird, einschließlich „Newline“. Wenn du mehrere Flags in einem Ausdruck verwenden möchtest, fasse sie einfach in der selben Anweisung zusammen: (?is) würde Groß-/Kleinschreibung ignorieren und der Punkt würde alle Zeichen finden. Es spielt keine Rolle, welches Flag du zuerst angibst: (?si) würde das selbe bewirken wie die Kombination oben.

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

Konvertierungen

Lass uns mit den Umwandlungseinstellungen beginnen. Im Teil Search & replace kannst du eine Regexp (kurz für Regular expression) eingeben, der die Zeichenkette beschreibt, die während der Umwandlung ersetzt wird. Der angenehme Teil ist der Assistent. Klicke auf den Zauberstab und du erhälst eine Vorschau dessen, was Calibre während des Umwandlungprozesses „sieht“. Scrolle zu der Zeichenkette, die du entfernen möchtest, wähle sie aus, kopiere sie und füge sie in das Regexp-Feld oben im Fenster ein. Wenn es variable Teile gibt, wie Seitenzahlen oder ähnliches, verwende Mengen und Quantifizierer um diese abzudecken und wenn du schon mal dabei bist: Denke daran Sonderzeichen zu maskieren, falls es welche gibt. Drücke auf die Schaltfläche Test und Calibre hebt die Teile hervor, die ersetzt würden, wenn du diesen Regexp anwendest. Sobald du zufrieden bist, drücke OK und starte den Prozess. Sei vorsichtig careful wenn deine Quelle mit Tags wie in diesem Beispiel versehen ist:

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

The last part is regular expression Search and replace in metadata fields. You can access this by selecting multiple books in the library and using bulk metadata edit. Be very careful when using this last feature, as it can do Very Bad Things to your library! Doublecheck that your expressions do what you want them to using the test fields, and only mark the books you really want to change! In the regular expression search mode, you can search in one field, replace the text with something and even write the result into another field. A practical example: Say your library contained the books of Frank Herbert’s Dune series, named after the fashion Dune 1 - Dune, Dune 2 - Dune Messiah and so on. Now you want to get Dune into the series field. You can do that by searching for (.*?) \d+ - .* in the title field and replacing it with \1 in the series field. See what I did there? That’s a reference to the first group you’re replacing the series field with. Now that you have the series all set, you only need to do another search for .*? - in the title field and replace it with "" (an empty string), again in the title field, and your metadata is all neat and tidy. Isn’t that great? By the way, instead of replacing the entire field, you can also append or prepend to the field, so, if you wanted the book title to be prepended with series info, you could do that as well. As you by now have undoubtedly noticed, there’s a checkbox labeled Case sensitive, so you won’t have to use flags to select behaviour here.

Well, that just about concludes the very short introduction to regular expressions. Hopefully I’ll have shown you enough to at least get you started and to enable you to continue learning by yourself- a good starting point would be the Python documentation for regexps.

One last word of warning, though: Regexps are powerful, but also really easy to get wrong. calibre provides really great testing possibilities to see if your expressions behave as you expect them to. Use them. Try not to shoot yourself in the foot. (God, I love that expression…). But should you, despite the warning, injure your foot (or any other body parts), try to learn from it.

Danksagung

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

  • ldolse

  • kovidgoyal

  • chaley

  • dwanthny

  • kacir

  • Starson17

  • Orpheu

For more about regexps see The Python User Manual. The actual regular expression library used by calibre is: regex which supports several useful enhancements over the Python standard library one.