Vše o používání regulárních výrazů v Calibre

Regulární výrazy jsou funkce používané na mnoha místech v Calibre pro provádění propracovaného zpracování obsahu e-knihy a metadat. Tento kurz je mírný úvod do problematiky používání regulárních výrazů v Calibre.

Nejdříve malé varování a malé povzbuzení

Toto bude nevyhnutelně poněkud technické – ostatně regulární výrazy jsou technickým nástrojem pro to technické záležitosti. Budu muset použít nějaký žargon a pojmy, které se mohou zdát složité a spletité. Budu se snažit vysvětlit tyto pojmy co nejjasněji, ale opravdu není možné obejít se bez jejich použití. Jak již bylo řečeno, nenechte se odradit žádným žargonem, protože se snažím vysvětlit všechno nové. A zatímco samotné regulární výrazy vám můžou připadat jako tajemná, černé magie (nebo, abychom byli prozaičtější, náhodný řetězec nesmyslných písmen a znaků), slibuji, že nejsou vůbec tak složité. Dokonce i ti, kteří pochopili regulární výrazy opravdu dobře, mají problémy se čtením těch složitějších, ale jejich psaní není tak obtížně – vytváříte výraz krok za krokem. Takže vykročte a následujte mě do králičí nory.

Kde v Calibre můžete použít regulární výrazy?

Existuje několik míst, kde Calibre používá regulární výrazy. Je zde Najít a nahradit ve volbách převodu, detekce metadat z názvů souborů v nastavení importu a Najít a nahradit při hromadné úpravě metadat knih. Editor knih Calibre může také použít regulární výrazy ve své funkci Najít a nahradit.

Co to u všech všudy je regulární příkaz?

Regulární výraz je způsob, jak popsat sady řetězců. Jeden regulární výraz může odpovídat velkému počtu různých řetězců. Proto jsou regulární výrazy tak mocné – jsou stručným způsobem popisu potenciálně velkého počtu variací.

Poznámka

Řetězec zde používám ve smyslu, v jakém se používá v programovacích jazycích: řetězec jednoho nebo více znaků, znaků včetně skutečných znaků, čísel, interpunkčních znamének a takzvaných prázdných znaků (konce řádků, tabulátorů atd). Pamatujte, že velká a malá písmena nejsou obecně považována za stejná, tedy „a“ je jiný znak než „A“ a tak dále. V Calibre regulární výrazy rozlišují velikost písmen v Panelu hledání, ale ne ve volbách převodu. Existuje způsob, aby každý regulární výraz rozlišoval velikost písmen, ale to probereme později. Trochu se to komplikuje, protože regulární výrazy umožňují variace v řetězcích, se kterými se porovnávají, takže jeden výraz může odpovídat více řetězcům, což je důvod, proč se je lidé vůbec obtěžují používat. Více si o tom povíme za chvíli.

Zajímá vás vysvětlení?

No, proto jsme tady. Za prvé, toto je nejdůležitější koncept v regulárních výrazech: Řetězec sám o sobě je regulární výraz, který odpovídá sobě samému. To znamená, že když chci pomocí regulárního výrazu porovnat řetězec "Hello, World!", použitý regulární výraz by byl Hello, World!. Ano, opravdu je to tak jednoduché. Ale všimněte si, že toto odpovídá pouze přesnému řetězci "Hello, World!", ne například "Hello, wOrld!" nebo "hello, world!" nebo jakékoliv jiné takové variaci.

To nezní tak špatně. Co dál?

Dál začínají opravdu dobré věci. Pamatujete si, jak jsem říkal, že regulární výrazy mohou odpovídat více řetězcům? Teď to začne být trochu složitější. Řekněme, jako poněkud praktičtější cvičení, že e-kniha, kterou chcete převést, má ošklivé zápatí počítající stránky, jako „Strana 5 z 423“. Je zřejmé, že číslo stránky bude růst od 1 do 423, takže budete muset porovnat 423 různých řetězců, že? Špatně, ve skutečnosti: regulární výrazy vám umožňují definovat sady znaků, které jsou porovnávány. Abyste definovali sadu, dejte všechny znaky, které chcete mít v sadě, do hranatých závorek. Takže například sada [abc] by odpovídala buď znaku „a“, „b“ nebo „c“. Sady budou vždy porovnávat pouze jeden ze znaků v sadě. „Rozumějí“ rozsahům znaků, takže pokud chcete porovnat všechna malá písmena, použili byste sadu [a-z], pro malá a velká písmena byste použili [a-zA-Z] a tak dále. Chápete to? Takže samozřejmě pomocí výrazu Strana [0-9] z 423 byste byli schopni porovnat prvních 9 stránek, čímž se sníží počet potřebných výrazů na tři: Druhý výraz Strana [0-9][0-9] z 423 by porovnal všechna dvojciferná čísla stránek, a jsem si jistý, že umíte odhadnout, jak by vypadal třetí výraz. Ano, do toho. Napište ho.

Parádní! Začíná to dávat smysl!

Doufal jsem, že to řeknete. Ale připravte se, teď to bude ještě lepší! Právě jsme viděli, že pomocí sad jsme mohli porovnat jeden z několika znaků najednou. Ale můžete dokonce opakovat znak nebo sadu, čímž snížíte počet výrazů potřebných pro zpracování výše uvedeného příkladu s čísly stránek na jeden. Ano, JEDEN! Jste nadšení? Měli byste být! Funguje to takhle: Některé takzvané speciální znaky, „+“, „?“ a „*“, opakují jednotlivý předcházející prvek. (Prvek znamená buď jeden znak, sadu znaků, řídící sekvenci nebo skupinu (o těch posledních dvou se budeme učit později.) – zkrátka každý jednotlivý prvek v regulárním výrazu) Tyto znaky se nazývají zástupné znaky nebo kvantifikátory. Abychom byli přesnější, „?“ odpovídá 0 nebo 1 výskytů předcházejícího prvku, „*“ odpovídá 0 nebo více výskytů předcházejícího prvku a „+“ odpovídá 1 nebo více výskytů předcházejícího prvku. Několik příkladů: Výraz a? by odpovídal buď „“ (což je prázdný řetězec, není nezbytně užitečný v tomto případě), nebo „a“, výraz a* by odpovídal „“, „a“, „aa“ nebo libovolnému počtu a v řadě, a konečně výraz a+ by odpovídal „a“, „aa“ nebo libovolnému počtu a v řadě (Poznámka: neodpovídal by prázdnému řetězci!). To samé pro sady: Výraz [0-9]+ by odpovídal každému existujícímu celému číslu! Vím, co si myslíte, a máte pravdu: Když toto použijete ve výše uvedeném případu porovnávání čísel stránek, nebyl by to jediný výraz pro porovnání všech čísel stránek? Ano, výraz Strana [0-9]+ z 423 by porovnal každé číslo stránky v této knize!

Poznámka

Poznámka o těchto kvantifikátorech: Obvykle se snaží porovnat tolik textu, kolik je možné, takže buďte opatrní při jejich použití. Nazývá se to „chamtivý chování“ – jsem si jistý, že chápete proč. Je to komplikovanější, když, řekněme, chcete porovnat značku. Vezměme například řetězec "<p class="calibre2">Tady je nadpis</p>" a řekněme, že byste chtěli porovnat otevírací značku (část mezi první dvojicí hranatých závorek, značky probereme trochu více později). Mysleli byste si, že by této značce odpovídal výraz <p.*>, ale ve skutečnosti odpovídá celému řetězci! (Znak „.“ je další speciální znak. Odpovídá čemukoliv kromě koncům řádků, takže v podstatě výraz .*` by odpovídal každičkému řádku, který si dokážete představit.) Místo toho zkuste použít ``<p.*?>, čímž přestane být kvantifikátor "*" chamtivým. Tento výraz bude odpovídat pouze první otevírací značce, jak bylo zamýšleno. Existuje vlastně další způsob, jak toho dosáhnout: Výraz <p[^>]*> bude odpovídat té samé otevírací značce – po další části uvidíte proč. Jen vezměte na vědomí, že dost často existuje více než jeden způsob, jak napsat regulární výraz.

No, tyto speciální znaky jsou velice elegantní a tak, ale co když chci porovnat tečku nebo otazník?

Samozřejmě to můžete udělat: Stačí před jakýkoliv speciální znak vložit zpětné lomítko a je interpretován jako prostý znak bez jakéhokoliv speciálního významu. Tento pár zpětného lomítka následovaného jedním znakem se nazývá řídící sekvence, a vložení zpětného lomítka před speciální znak se nazývá uvozování toho znaku. Řídící sekvence je interpretována jako jeden prvek. Existují samozřejmě řídící sekvence, které dělají více než jen uvozování speciálních znaků, například "\t" znamená tabulátor. K některým řídícím sekvencím se dostaneme později. Jo a mimochodem, pokud jde o tyto speciální znaky: Uvažujte o kterémkoliv znaku, o kterém se budeme v tomto úvodu bavit, že má nějakou funkci, která je speciální, a tedy musí být uvozen, pokud chcete prostý znak.

Takže, jaké jsou nejužitečnější sady?

Věděl jsem, že se zeptáte. Některé užitečné sady jsou [0-9] odpovídající jednomu číslu, [a-z] odpovídající jednomu malému písmenu, [A-Z] odpovídající jednomu velkému písmenu, [a-zA-Z] odpovídající jednomu písmenu a [a-zA-Z0-9] odpovídající jednomu písmenu nebo číslu. Můžete také použít řídící sekvenci jako zjednodušení:

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

Poznámka

„Prázdný znak“ je termín pro cokoliv, co nebude vytištěno. Tyto znaky zahrnují mezeru, tabulátor, posun řádku, posun strany a návrat na začátek řádku.

Jako poslední poznámka o sadách, sadu můžete také definovat jako jakýkoliv znak kromě těch v sadě. Uděláte to zahrnutím znaku "^" jako úplně prvního znaku v sadě. Takže [^a] by odpovídalo libovolnému znaku kromě „a“. Tomu se říká doplňující sada. Zkratky řídící sekvence, které jsme viděli dříve, mohou být také doplněny: "\D" znamená jakýkoli nečíselný znak, takže je ekvivalentem [^0-9]. Jak už jste asi uhodli, ostatní zkratky mohou být doplněny pomocí příslušného velkého písmena namísto malého. Takže když se vrátíme k příkladu <p[^>]*> z předchozí části, můžete nyní vidět, že použitá sada znaků se pokusí porovnat jakýkoliv znak kromě uzavírací úhlové závorky.

Ale když budu mít několik různých řetězců, které chci porovnat, zkomplikuje se to?

Nebojte se, život je pořád jednoduchý. Vezměme si tento příklad: Kniha, kterou převádíte má napsán „Název“ na každé liché stránce a „Autor“ na každé sudé stránce. V tisku to vypadá skvěle, že jo? Ale v e-knize je to otravné. Můžete seskupit celé výrazy do normálních závorek a znak "|" vám umožní porovnat buď výraz s jeho pravou nebo levou stranou. Toto zkombinujte a máte hotovo. Je to na vás příliš? Dobře, nejdříve seskupíme výrazy pro sudé a liché stránky, takže získáme (Název)(Autor) jako naše dva potřebné výrazy. Nyní věci zjednodušíme pomocí svislé čáry ho pruhu (znak "|" se nazývá svislá čára): Pokud použijete výraz (Název|Autor), dostanete shodu buď pro „Název“ (na lichých stránkách), nebo pro „Autor“ (na sudých stránkách). No, nebylo to jednoduché?

Svislou čáru můžete samozřejmě použít také bez použití seskupovacích závorek. Pamatujete, když jsem říkal, že kvantifikátory opakují jim předcházející prvek? No, svislá čára funguje trochu jinak: Výraz „Název|Autor“ bude také odpovídat buď řetězci „Název“ nebo řetězci „Autor“, stejně jako výše uvedený příklad používající seskupování. Svislá čára vybírá mezi celým výrazem, který ji předchází a následuje. Takže pokud byste chtěli porovnat řetězce „Calibre“ a „calibre“ a chtěli byste vybrat pouze mezi malým a velkým „c“, museli byste použít výraz (c|C)alibre, kde seskupení zajišťuje, že bude vybráno pouze „c“. Pokud byste použili c|Calibre, získali byste shodu na řetězec „c“ nebo na řetězec „Calibre“, což není to, co jsme chtěli. Stručně řečeno: Pokud si nejste jisti, použijte seskupování společně se svislou čárou.

Zapomněl jsi…

… počkejte chvilku, je tu ještě jedna poslední, opravdu užitečná věc, kterou můžete dělat se skupinami. Pokud máte skupinu, kterou jste předtím porovnal, můžete použít odkazy na tuto skupinu později ve výrazu: Skupiny jsou číslovány od 1, a odkazujete na ně uvozením čísla skupiny, na kterou chcete odkázat, tedy pátá skupina by byla odkázána \5. Takže pokud jste hledali ([^ ]+) \1 v řetězci „Test Test“, porovnali byste celý řetězec!

Na začátku jsi říkal, že existuje způsob, aby regulární výraz nerozlišoval malá a velká písmena?

Ano, říkal, díky za věnování pozornosti a připomenutí. Můžete říct Calibre, jak chcete řešit některé věci pomocí tzv. vlajek. Vlajky zahrnete do svého výrazu pomocí speciální konstrukce (?sem přijdou vlajky), kde byste samozřejmě nahradili „sem přijdou vlajky“ konkrétními vlajkami, které chcete. Pro ignorování velikosti písmen je vlajka i, takže zahrnete do svého výrazu (?i). Takže (?i)test by odpovídal „Test“, „tEst“, „TEst“ a jakémukoliv případu variace si dokážete vymyslet.

Další užitečná vlajka umožňuje, aby tečka odpovídala všem libovolným znakům, včetně nového řádku, a to vlajka s. Pokud chcete použít ve výrazu více vlajek, stačí je dát do stejného příkazu: (?is) by ignorovalo velikost písmen a tečka by odpovídala všemu. Nezáleží na tom, kterou vlajkou uvedete první, (?si) by bylo ekvivalentem výše uvedeného.

Myslím, že teď už začínám ty regulární výrazy chápat… Jak je použiju v Calibre?

Převody

Začněme s nastavením převodu, což je opravdu elegantní. Do části Hledat a nahradit můžete zadat regulární výraz, který popisuje řetězec, který bude při převodu nahrazen. Elegantní na tom je průvodce. Klikněte na kouzelnickou hůlku a získáte náhled toho, co Calibre „vidí“ během procesu převodu. Přejděte dolů na řetězec, který chcete odebrat, vyberte ho a zkopírujte, vložte ho do pole regulárního výrazu v horní části okna. Pokud jsou zde proměnné části, jako jsou čísla stránek nebo tak, použijte pro jejich pokrytí sady a kvantifikátory, a když už jsme u toho, nezapomeňte uvodit speciální znaky, pokud tu nějaké jsou. Stiskněte tlačítko označené Testovat a Calibre zvýrazní části, které budou nahrazeny, pokud použijete regulární výraz. Jakmile jste spokojeni, klikněte na tlačítko OK a proveďte převod. Buďte opatrní, pokud váš zdroj převodu obsahuje značky, jako tento příklad:

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

(nestydatě vytrženo z tohoto vlákna). Museli byste také odstranit některé značky. V tomto příkladu bych doporučoval začít značkou <b class="calibre2">, nyní budete muset skončit odpovídající uzavírací značkou (otevírací značky jsou <značka>, uzavírací značky jsou </značka>), což je v tomto případě jednoduše následující </b>. (Podívejte se do dobré příručky k HTML nebo se zeptejte ve fóru, pokud si v tomto bodě nejste jisti.) Počáteční značku lze popsat pomocí <b.*?>, uzavírací značku pomocí </b>, takže bychom mohli odstranit vše mezi těmito značkami pomocí <b.*?>.*?</b>. Ale použití tohoto výrazu by byl špatný nápad, protože odstraňuje vše, co je uzavřeno značkami <b> (který mimochodem vykreslí uzavřený text tučným písmem), a vsaďte se, že tímto způsobem odstraníme části knihy. Místo toho zahrňte i začátek uzavřeného řetězce, takže regulární výraz bude <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b>. \s s kvantifikátory jsou zde zahrnuty namísto výslovného použití mezer, jak je vidět v řetězci, aby byly zachyceny všechny variace řetězce, které by mohly nastat. Nezapomeňte zkontrolovat, co Calibre odstraní, když testuje nový výraz, abyste se ujistili, že neodstraníte žádné části, které chcete zachovat. Pokud zkontrolujete pouze jeden výskyt, může vám uniknout neshoda někde jinde v textu. Také pamatujte, že kdybyste omylem odstranili více nebo méně značek, než jste doopravdy chtěli, Calibre se pokusí opravit poškozený kód po provedení odstranění.

Přidávání knih

Další věc, ke které můžete použít regulární výrazy, je extrahování metadat z názvů souborů. Tuto funkci můžete najít v části nastavení „Přidávání knih“. Je zde speciální funkce: Můžete použít názvy polí pro pole metadat, například (?P<title&gt;) by znamenalo, že Calibre použije tuto část řetězce jako název knihy. Povolené názvy polí jsou uvedeny v oknech, společně s dalším pěkným testovacím polem. Příklad: Řekněme, že chcete importovat spoustu souborů s názvem jako je Klasické texty: Božská komedie, Dante Alighieri.mobi. (Samozřejmě, že tu už ve své knihovně máte, protože všichni milujeme klasickou italskou poezii) nebo Sci-fi eposy: Trilogie Nadace, Isaac Asimov.epub. To je samozřejmě schéma názvů, ze kterého by Calibre nevyextrahovalo žádná smysluplná data – standardní výraz pro extrahování metadat je (?P<title>.+) - (?P,author>[^_]+). Regulární výraz, který by tu fungoval, by byl [a-zA-Z]+: (?P<title>.+), (?P<author>.+). Pamatujte, že uvnitř skupiny pro pole metadat musíte použít výrazy k popisu toho, čemu pole skutečně odpovídá. A také pamatujte, že při použití testovacího pole, které Calibre poskytuje, musíte přidat příponu souboru k testovanému souboru, jinak nezískáte vůbec žádnou shodu, i když použijete fungující výraz.

Hromadná úprava metadat

Poslední část je hledání a nahrazování v polích metadat pomocí regulárního výrazu. K tomuto se můžete dostat výběrem více knih v knihovně a použitím hromadné úpravy metadat. Buďte velmi opatrní při používání této poslední funkce, protože může způsobit velice špatné věci vaší knihovně! Překontrolujte, že vaše výrazy dělají to, co chcete, pomocí testovacích polí, a označte pouze knihy, které opravdu chcete změnit! V režimu hledání regulárními výrazy můžete hledat v jednom poli, nahradit text něčím a dokonce zapsat výsledek do jiného pole. Praktický příklad: Řekněme, že vaše knihovna obsahuje knihy série Duna Franka Herberta pojmenované po vzoru Duna 1 - Duna, Duna 2 - Spasitel Duny a tak dále. Nyní chcete dostat Duna do pole série. Můžete to udělat hledáním (.*?) \d+ - .* v poli názvu a nahrazením \1 v poli série. Vidíte, co jsem udělal? To je odkaz na první skupinu, kterou nahrazujete pole série. Nyní, když máte sérii nastavenou, potřebujete jen provést další vyhledávání .*? - v poli názvu a nahrazením "" (prázdný řetězec) opět v poli názvu, a vaše metadata jsou jsou pěkně čistá a uklizená. Není to skvělé? Mimochodem, namísto nahrazení celého pole můžete také poli připojit nebo předřadit, takže pokud chcete, aby byla názvu knihy předřazena informace o sérii, mohli byste to udělat stejně. Jak jste si už nepochybně všimli, je zde zaškrtávací políčko označené Rozlišovat malá a velká, takže zde nebudete muset používat vlajky pro výběr chování.

No, je asi na čase uzavřít velice krátký úvod do regulárních výrazů. Doufám, že jsem vám ukázal dost, abyste alespoň mohli začít, a umožnil vám pokračovat v samostatném učení – dobrým výchozím bodem je Dokumentace Pythonu pro regulární výrazy.

Ale ještě jedna poslední varování: Regulární výrazy jsou výkonné, ale také je velice snadné je pokazit. Calibre poskytuje opravdu skvělé možnosti pro testování, abyste viděli, jestli se vaše výrazy chovat podle očekávání. Používejte je. Pokuste se nestřelit se do nohy. (Bože, tenhle výraz miluju…) Ale kdybyste si i přes varování poranili nohu (nebo jiné části těla), pokuste se z toho poučit.

Poděkování

Díky za pomoc s tipy, opravami a tak:

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

Více o regulárních výrazech najdete v Uživatelské příručce Pythonu.