Allt om att använda reguljära uttryck i calibre

Reguljära uttryck används på många ställen i calibre för avancerad bearbetning av e-böckers innehåll och metadata. Den här handledningen ger en lättillgänglig introduktion som hjälper dig att komma igång med reguljära uttryck i calibre.

Först några varnande och uppmuntrande ord

Detta blir ofrånkomligen ganska tekniskt – reguljära uttryck är trots allt ett tekniskt verktyg för tekniska uppgifter. Jag behöver använda en del facktermer och begrepp som kan verka komplicerade eller invecklade. Jag ska försöka förklara dem så tydligt som möjligt, men kan inte helt undvika dem. Låt dig därför inte avskräckas av facktermerna; jag har försökt förklara allt nytt. Reguljära uttryck kan verka som svårbegriplig svartkonst – eller, mer prosaiskt, som en slumpmässig blandning av bokstäver och tecken – men jag lovar att de inte är så komplicerade. Även den som behärskar reguljära uttryck väl kan ha svårt att läsa de mer avancerade, men det är inte lika svårt att skriva dem: du bygger uttrycket steg för steg. Ta därför första steget och följ med ner i kaninhålet.

Var i calibre kan man använda reguljära uttryck?

calibre använder reguljära uttryck på flera ställen: i Sök och ersätt bland konverteringsalternativen, vid identifiering av metadata från filnamn i importinställningarna och i Sök och ersätt vid massredigering av metadata. Bokredigeraren använder också reguljära uttryck i funktionen Sök och ersätt. Slutligen kan du använda dem när du söker i calibres boklista och i e-bokvisaren.

Vad i hela världen är ett reguljärt uttryck?

Ett reguljärt uttryck är ett sätt att beskriva mängder av strängar. Ett enda reguljärt uttryck kan matcha flera olika strängar. Det är detta som gör reguljära uttryck så kraftfulla: de beskriver ett potentiellt stort antal variationer på ett kortfattat sätt.

Anteckning

Jag använder sträng här i den mening den används i programspråk: en sträng av ett eller flera tecken, tecken inklusive faktiska tecken, siffror, skiljetecken och s.k. whitespace (radbrytningar, tabulatorer etc.). Observera att i allmänhet anses inte stora och små bokstäver vara samma, alltså ”a” som ett annat tecken från ”A” och så vidare. I calibre, är reguljära uttryck skiftlägesokänslig i sökfältet, men inte i konverteringsalternativet. Det finns ett sätt att göra alla reguljära uttryck skiftlägesokänsliga, men vi kommer att diskutera det senare. Det blir komplicerat eftersom reguljära uttryck tillåter variationer i strängar som matchar, så ett uttryck kan matcha flera strängar, vilket är varför folk bryr sig att använda dem. Mer om det om ett tag.

Har du lust att förklara?

Tja, det är därför vi är här. För det första är detta det viktigaste konceptet i reguljära uttryck: En sträng i sig är ett reguljärt uttryck som matchar sig själv. Det vill säga, om jag ville matcha strängen "Hej, Värld!" genom att använda ett reguljärt uttryck, skulle det reguljära uttrycket för att använda vara Hej, Värld!. Och ja, det är verkligen så enkelt. Du kommer att märka dock att detta endast matchar den exakta strängen "Hej, Värld!", inte t.ex. "Hej vÄrlden!" eller "hej, värld!" eller någon annan sådan variation.

Det låter inte så illa. Vad kommer härnäst?

Nästa är början på riktigt bra grejer. Kom ihåg var jag sa att reguljära uttryck kan matcha flera strängar? Det är här det blir lite mer komplicerat. Säg, som en något mer praktisk övning, e-boken du ville konvertera hade en otäck sidfot som räknar sidor, ”Sida 5 av 423”. Uppenbarligen skulle sidnumret stiga från 1 till 423, så du skulle behöva matcha 423 olika strängar, eller hur? Fel, faktiskt: reguljära uttryck tillåter dig definiera uppsättningar av tecken som matchar: För att definiera en uppsättning, lägger du alla de tecken du vill att ska vara i uppsättningen inom hakparenteser. Så, till exempel uppsättningen [abc] skulle matcha antingen tecknet ”a”, ”b” eller ”c”. Uppsättningarna kommer alltid bara matcha en av karaktärerna i uppsättningen. De ”förstår” teckenformattering, det vill säga om du ville matcha mot alla små bokstäver, skulle du använda uppsättningen [a-z] för gemener och för versaler skulle du använda [a-zA-Z] osv. Förstår du principen? Så, självklart, med hjälp av uttrycket Sida [0-9] av 423 skulle du kunna matcha de första nio sidorna, vilket minskar uttrycken som behövs för tre: Det andra uttrycket Sida [0-9][0-9] av 423 skulle matcha alla tvåsiffriga sidnummer, och jag är säker på att du kan gissa hur det tredje uttryck skulle se ut. Ja, gå vidare. Skriv ner det.

Snyggt! Nu börjar det klarna!

Jag hoppades att du skulle säga det. Men håll i dig, för nu blir det ännu bättre! Vi såg nyss att en teckenuppsättning kan matcha ett av flera tecken samtidigt. Du kan dessutom upprepa ett tecken eller en teckenuppsättning och därmed minska antalet uttryck som behövs för sidnummerexemplet ovan till ett enda. Ja, ETT! Visst är det spännande? Så här fungerar det: Specialtecknen ”+”, ”?” och ”*” upprepar det enskilda element som står närmast före dem. Ett element kan vara ett enskilt tecken, en teckenuppsättning, en escape-sekvens eller en grupp (vi återkommer till de två sistnämnda senare) – kort sagt en enskild enhet i ett reguljärt uttryck. Tecknen kallas jokertecken eller kvantifierare. Mer exakt matchar ”?” 0 eller 1 förekomst av det föregående elementet, ”*” matchar 0 eller flera och ”+” matchar 1 eller flera. Några exempel: Uttrycket a? matchar antingen ”” (den tomma strängen, som inte är särskilt användbar i just detta fall) eller ”a”. Uttrycket a* matchar ””, ”a”, ”aa” eller valfritt antal a i följd. Slutligen matchar a+ ”a”, ”aa” eller valfritt antal a i följd (observera att det inte matchar den tomma strängen). Detsamma gäller teckenuppsättningar: Uttrycket [0-9]+ matchar vilket heltal som helst! Jag vet vad du tänker, och du har rätt: Om du använder det i sidnummerexemplet ovan, blir inte detta då det enda uttryck som behövs för att matcha alla sidnummer? Jo, uttrycket Page [0-9]+ of 423 matchar varje sidnummer i boken!

Anteckning

En kommentar om dessa kvantifierare: De försöker i allmänhet att matcha så mycket text som möjligt, så var försiktig när du använder dem. Detta kallas ”girigt beteende” - Jag är säker på att du förstår varför. Det blir problematiskt när man, säg, försöker matcha en tagg. Tänk dig, till exempel strängen "<p class="calibre2">Titel här</p>" och låt oss säga att du skulle vilja matcha öppningstaggen (delen mellan det första paret vinkelfästen, lite mer om taggar senare). Man skulle kunna tro att uttrycket <p.*> skulle matcha den taggen men faktiskt, den matchar hela strängen! (Tecknet ”.” är ett annat specialtecken. Det matchar allt utom radbrytningar, så uttrycket .* matchar i princip vilken enskild rad som helst. Använd i stället <p.*?> för att göra kvantifieraren "*" icke-girig. Uttrycket matchar då endast den första öppningstaggen, som avsett. Det finns faktiskt ett annat sätt att åstadkomma detta: Uttrycket <p[^>]*> matchar denna öppningstagg- du kommer se varför efter nästa avsnitt. Bara konstatera att det ganska ofta finns mer än ett sätt att skriva ett reguljärt uttryck.

De här specialtecknen är praktiska, men hur matchar jag en punkt eller ett frågetecken?

Det kan du naturligtvis göra. Sätt bara ett omvänt snedstreck framför ett specialtecken, så tolkas det som det bokstavliga tecknet utan någon särskild betydelse. Ett omvänt snedstreck följt av ett enda tecken kallas en escape-sekvens, och att sätta ett omvänt snedstreck framför ett specialtecken kallas att maskera tecknet. En escape-sekvens tolkas som ett enda element. Det finns även escape-sekvenser som gör mer än att bara maskera specialtecken; "\t" betyder till exempel en tabulator. Vi återkommer till några av escape-sekvenserna senare. Betrakta för övrigt alla tecken som beskrivs som specialtecken i den här introduktionen som tecken med en särskild funktion, vilket innebär att de måste maskeras när du vill matcha själva tecknet.

Så, vilka är de användbaraste uppsättningarna?

Jag visste att du skulle fråga. Några användbara teckenuppsättningar är [0-9], som matchar en enda siffra, [a-z], som matchar en enda gemen, [A-Z], som matchar en enda versal, [a-zA-Z], som matchar en enda bokstav, och [a-zA-Z0-9], som matchar en enda bokstav eller siffra. Du kan också använda en escape-sekvens som kortform:

\d

motsvarar [0-9]

\w

motsvarar [a-zA-Z0-9_]

\s

motsvarar valfritt blanktecken

Anteckning

”Whitespace” är en term för allt som inte kommer att skrivas ut. Dessa tecken inkluderar blanksteg, tabulator, radmatning, formulärmatning, vagnretur, icke-brytande blanksteg etc.

Anteckning

Uppsättningarna för versaler och gemener kan matcha båda skiftlägena om skiftlägesokänslig sökning är aktiverad. Sådana inställningar finns till exempel under Inställningar->Sökning i calibre, i sökpanelen i calibres e-bokvisare och i verktyget Redigera bok.

Som en sista anmärkning om teckenuppsättningar kan du definiera en uppsättning som matchar alla tecken utom dem som ingår i uppsättningen. Det gör du genom att placera tecknet "^" som det allra första tecknet i uppsättningen. [^a] matchar alltså alla tecken utom ”a”. Detta kallas att komplementera uppsättningen. De kortformer med escape-sekvenser som vi såg tidigare kan också komplementeras: "\D" betyder ett valfritt tecken som inte är en siffra och motsvarar därför [^0-9]. De andra kortformerna komplementeras genom att den gemena bokstaven byts mot motsvarande versal. I exemplet <p[^>]*> från föregående avsnitt försöker teckenuppsättningen alltså matcha alla tecken utom en avslutande vinkelparentes.

Men om jag hade några olika strängar jag ville matcha, blir saker komplicerade?

Frukta inte, livet är fortfarande bra och enkelt. Tänk på detta exempel: Boken du konverterar har ”Titel” skrivet på varje udda sida och ”Författare” skrivet på varje jämn sida. Ser bra i tryck, eller hur? Men i e-böcker är det irriterande. Du kan gruppera hela uttryck i normala parenteser och tecknet "|" låter dig matcha antingen uttrycket till höger eller det till vänster. Kombinera dem så är du klar. För snabbt för dig? Okej, först och främst grupperar vi uttryck för udda och jämna sidor och på så sätt får (Titel)(Författare) som våra två nödvändiga uttryck. Nu gör vi det enklare genom att använda det lodräta strecket ("|" kallas vertikala stapeltecknet): Om du använder uttrycket (Titel|Författare) får du antingen en träff för ”Titel” (på udda sidorna) eller så får du en träff för ”Författare” (på jämna sidorna). Var det inte lätt?

Du kan naturligtvis också använda det lodräta strecket utan grupperingsparenteser. Minns du att kvantifierare upprepar elementet framför dem? Det lodräta strecket fungerar annorlunda: Uttrycket ”Titel|Författare” matchar antingen strängen ”Titel” eller strängen ”Författare”, precis som i grupperingsexemplet ovan. Det lodräta strecket väljer mellan hela uttrycket före och hela uttrycket efter strecket. Om du vill matcha strängarna ”Calibre” och ”calibre” och bara välja mellan versalt och gement ”c” måste du därför använda uttrycket (c|C)alibre. Grupperingen gör att endast ”c” väljs. Med c|Calibre skulle uttrycket i stället matcha strängen ”c” eller strängen ”Calibre”. Kort sagt: använd gruppering tillsammans med det lodräta strecket när du är osäker.

Du missade…

… vänta lite, det finns en sista riktigt användbar sak du kan göra med grupper. Om du tidigare har matchat en grupp kan du hänvisa till den senare i uttrycket. Grupperna numreras från 1, och du hänvisar till en grupp genom att skriva ett omvänt snedstreck framför gruppnumret. Den femte gruppen anges alltså som \5. Om du söker efter ([^ ]+) \1 i strängen ”Test Test” matchas hela strängen!

I början sa du att det fanns ett sätt att göra ett reguljärt uttryck skiftlägesokänsligt?

Ja, tack för påminnelsen. Med så kallade flaggor kan du ange hur calibre ska behandla uttrycket. Flaggor skrivs med konstruktionen (?flags go here), där texten ersätts med de flaggor du vill använda. Flaggan i gör matchningen skiftlägesokänslig, så du skriver (?i) i uttrycket. (?i)test matchar exempelvis ”Test”, ”tEst”, ”TEst” och andra skiftlägesvarianter.

En annan användbar flagga, s, gör att punkten matchar alla tecken, även radbrytningar. Om du vill använda flera flaggor i ett uttryck placerar du dem i samma konstruktion: (?is) gör sökningen skiftlägesokänslig och låter punkten matcha alla tecken. Flaggornas ordning spelar ingen roll; (?si) motsvarar alltså uttrycket ovan.

Jag tror jag börjar förstå dessa reguljära uttryck nu… hur använder jag dem i calibre?

Konverteringar

Vi börjar med konverteringsinställningarna. Under Sök och ersätt kan du ange ett reguljärt uttryck som beskriver den text som ska ersättas under konverteringen. Det praktiska är guiden. Klicka på trollstaven för att visa en förhandsgranskning av det calibre ser under konverteringen. Bläddra till texten du vill ta bort, markera och kopiera den och klistra sedan in den i fältet för reguljära uttryck högst upp i fönstret. Om texten innehåller varierande delar, till exempel sidnummer, använder du teckenuppsättningar och kvantifierare för att matcha dem. Kom också ihåg att föregå specialtecken med ett omvänt snedstreck. Klicka på Test så markerar calibre de delar som skulle ersättas. När du är nöjd klickar du på OK och genomför konverteringen. Var försiktig om konverteringskällan innehåller taggar som i följande exempel:

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

(skamlöst hämtat från denna tråd). Du måste även ta bort några av taggarna. I det här exemplet rekommenderar jag att du börjar med taggen <b class="calibre2">. Därefter måste uttrycket avslutas med motsvarande sluttagg (öppningstaggar skrivs <tag> och sluttaggar </tag>), vilket i detta fall helt enkelt är nästa </b>. (Läs en bra HTML-handbok eller fråga i forumet om detta är oklart.) Öppningstaggen kan beskrivas med <b.*?> och sluttaggen med </b>. Vi skulle alltså kunna ta bort allt mellan taggarna med <b.*?>.*?</b>. Det är dock en dålig idé att använda det uttrycket, eftersom det tar bort allt som omges av <b>-taggar (vilka för övrigt visar den omslutna texten med fetstil), och då riskerar vi att ta bort delar av boken. Ta därför även med början av den omslutna strängen och använd det reguljära uttrycket <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b>. Här används \s tillsammans med kvantifierare i stället för de uttryckliga blanksteg som syns i strängen, så att eventuella variationer av strängen också fångas. Kontrollera alltid vad calibre tänker ta bort när du provar ett nytt uttryck, så att du inte råkar ta bort något du vill behålla. Om du bara kontrollerar en förekomst kan du missa en felaktig matchning någon annanstans i texten. Observera också att calibre försöker reparera den skadade koden efter borttagningen om du av misstag tar bort fler eller färre taggar än avsett.

Lägga till böcker

En annan sak du kan använda reguljära uttryck för är att extrahera metadata från filnamn. Du hittar den här funktionen i delen ”Lägga till böcker” i inställningarna. Det finns en speciell funktion här: Du kan använda fältnamn för metadatafält, till exempel (?P<title>) skulle indikera att calibre använder den här delen av strängen som boktitel. De tillåtna fältnamnen listas i fönstren, tillsammans med ett annat bra testfält. Ett exempel: Säg att du vill importera en hel massa filer med namnen Klassiska texter: Den gudomliga komedin av Dante Alighieri.mobi. (Detta finns uppenbarligen redan i ditt bibliotek, eftersom vi alla älskar klassisk italiensk poesi) eller Science Fiction-epos: Grundtrilogin av Isaac Asimov.epub. Detta är uppenbarligen ett namngivningsschema som calibre inte kommer att extrahera någon meningsfull data från - dess standarduttryck för att extrahera metadata är (?P<title>.+) - (?P<author>[^_]+). Ett reguljärt uttryck som fungerar här skulle vara [a-zA-Z]+: (?P<title>.+) by (?P<author>.+). Observera att du, inuti gruppen för metadatafältet, måste använda uttryck för att beskriva vad fältet faktiskt matchar. Och observera också att när du använder testfältet som calibre tillhandahåller måste du lägga till filändelsen till ditt testfilnamn, annars får du inga matchningar alls, trots att du använder ett fungerande uttryck.

Redigera metadata i grupp

Den sista användningen är Sök och ersätt med reguljära uttryck i metadatafält. Du når funktionen genom att markera flera böcker i biblioteket och välja massredigering av metadata. Var mycket försiktig när du använder denna funktion, eftersom den kan göra mycket stor skada i biblioteket! Kontrollera med hjälp av testfälten att uttrycken gör precis det du avser, och markera endast de böcker som verkligen ska ändras. I sökläget för reguljära uttryck kan du söka i ett fält, ersätta texten och till och med skriva resultatet till ett annat fält. Ett praktiskt exempel: Anta att biblioteket innehåller böckerna i Frank Herberts Dune-serie med titlar som Dune 1 - Dune, Dune 2 - Dune Messiah och så vidare. Du vill nu lägga in Dune i seriefältet. Sök då efter (.*?) \d+ - .* i titelfältet och ersätt med \1 i seriefältet. Ser du vad som händer? \1 är en referens till den första gruppen, vars innehåll skrivs till seriefältet. När serien är korrekt behöver du bara söka efter .*? - i titelfältet och ersätta det med "" (en tom sträng), återigen i titelfältet. Därefter är dina metadata prydliga och konsekventa. Visst är det praktiskt? I stället för att ersätta hela fältet kan du även lägga till text i början eller slutet. Om du exempelvis vill lägga serieinformationen före boktiteln kan du göra det. Som du säkert redan har sett finns även kryssrutan Skiftlägeskänslig, så du behöver inte använda flaggor för att styra detta beteende.

Därmed är den här mycket korta introduktionen till reguljära uttryck nästan slut. Förhoppningsvis har du fått tillräckligt med kunskap för att komma igång och fortsätta lära dig på egen hand. En bra utgångspunkt är Python-dokumentationen om reguljära uttryck.

Ett sista varningens ord: Reguljära uttryck är kraftfulla, men det är också mycket lätt att göra fel. calibre har utmärkta testfunktioner som visar om uttrycken fungerar som du förväntar dig. Använd dem. Försök att inte skjuta dig själv i foten. (Jag älskar verkligen det uttrycket.) Om du trots varningen ändå skadar foten, eller någon annan kroppsdel, försök att lära dig av misstaget.

Snabbreferens

Medverkande

Tack för hjälpen med tips, rättningar och sådant:

  • ldolse

  • kovidgoyal

  • chaley

  • dwanthny

  • kacir

  • Starson17

  • Orpheu

Mer information om reguljära uttryck finns i Python-dokumentationen. Det bibliotek för reguljära uttryck som faktiskt används av calibre är regex, som har flera användbara förbättringar jämfört med Pythons standardbibliotek.