calibre で正規表現を利用するにあたって重要なこと

正規表現は、電子書籍のコンテンツと書誌に対して高度な操作を実行するために calibre のあちこちで使われている機能です。このチュートリアルは、calibre で正規表現を使い始めるための簡単な入門書です。

最初に、警告と激励

必然的に、これは技術的なものになります。何と言っても正規表現というものは、技術的なことを行うための技術的なツールなのです。使わざるを得ない専門用語や概念が、複雑または難解に見えるかもしれません。そうした概念について、可能な限りわかりやすく説明していくつもりですが、専門用語をまったく使わずに説明するのは無理があります。そうは言っても、専門用語を見てもがっかりしないでください。新しいことはすべて説明するよう努めました。そして正規表現そのものがあやしげな黒魔術のように見えるかもしれませんが (もっとぶっちゃけると、わけのわからないランダムな文字と記号の羅列にしか見えないかもしれませんが) 決してそれほど複雑なものではありません。正規表現をきちんと理解している人でも複雑なものを解読するのは困難ですが、書くのはそれほど難しいことではありません。一歩ずつ段階的に構築していけばよいのです。ですから、一緒に最初の一歩を踏み出してみましょう。

calibre のどこで正規表現で使えますか?

calibre が正規表現を使用する場所はいくつかあります。それは変換オプションの中の 検索 & 置換 、インポート設定の中にあるファイル名からの書誌の検出、書誌を一括編集するときの検索 & 置換です。calibre の書籍エディタは、検索と置換 機能の中で正規表現を使用することができます。最後に、calibre の本の一覧を検索するときや calibre の電子書籍ビューアの中で検索をするときにも正規表現を使用できます。

正規表現とはいったい何ですか?

正規表現とは、文字列のセットを表現する方法です。ひとつの正規表現で、いくつもの異なる文字列に マッチ させることができます。これが正規表現を非常に強力にしているものです。潜在的に数多くのバリエーションを表現する、簡潔な方法なのです。

注釈

ここでは文字列という言葉をプログラミング言語で使用される意味で使っています。文字列とは、ひとつ以上の文字で、実際の文字、数字、句読点、およびいわゆる空白と呼ばれるもの (改行、タブなど) を含みます。通常、大文字と小文字は同じものとはみなされないため、たとえば "a" は "A" とは別の文字として扱われます。calibre では、検索バーでは正規表現は大文字と小文字を区別しませんが、変換オプションでは区別します。すべての正規表現で大文字と小文字を区別しなくする設定もありますが、それは後で説明します。正規表現はマッチする文字列にバリエーションを許しているために複雑になり、ひとつの表現が複数の文字列にマッチします。人々が正規表現を使用したがらないのは、それが理由です。

説明してもらえますか?

それこそが目的です。まず、正規表現において最も重要な概念ですが、 ある文字列は、その文字列自体にマッチする正規表現 です。つまり "Hello, World!" という文字列に正規表現を使ってマッチさせたい場合、使用すべき正規表現は Hello, World! です。そうです、それくらい簡単なことなのです。ただし、これがマッチするのは "Hello, World!" という文字列 だけ です。つまり、"Hello, wOrld!""hello, world!" など他のバリエーションにはマッチしません。

そんなに悪くありません。次は?

次は、本当に使えるもののさわりの部分です。正規表現が複数の文字列にマッチする可能性があると説明したのを覚えていますか? これが少し複雑になっていくところです。たとえば、もっと実用的な例として、変換したい電子書籍のフッタ部分に "Page 5 of 423" のように厄介なページカウントがあったとします。明らかにページ数は 1 から 423 まで順に増えていきますから、423 通りの異なる文字列にマッチさせる必要があります。そうでしょう? しかし、実は必要ないのです。正規表現を使えば、マッチする文字のセットを定義することが可能です。セットを定義するには、マッチさせたいすべての文字を角カッコの中に羅列します。したがってたとえば [abc] というセットは "a", "b" または "c" という文字にマッチします。セットは、常にセット内の文字のひとつとだけマッチします。セットは文字の範囲を "理解" します。つまり、すべての小文字とマッチさせたい場合には [a-z] を使用し、小文字と大文字にマッチさせたい場合には [a-zA-Z] を使用するといった具合です。理解できましたか? したがって Page [0-9] of 423 という表現を使用すれば、言うまでもなく最初の 9 ページにマッチさせることができます。こうして必要な表現を 3 個に絞ることができます。2 個目の表現は Page [0-9][0-9] of 423 で、すべての 2 桁のページにマッチします。3 個目の表現をどうすればよいかは、もうおわかりですね。はい、どうぞ。書いてみてください。

すごい! 意味がわかってきました!

そう言ってくれると思っていました。でも気を引き締めてください。まだまだよくなって行きます。セットを使用すると、いくつかの文字を一度にマッチさせることができることがわかりました。しかし文字やセットを繰り返すことも可能なのです。そうすることで、上の例でページ番号を扱う正規表現をひとつに減らすことができます。そうです、たったの 1 個です。わくわくしますか? きっとするでしょう。それは次のように機能します: 特殊文字と呼ばれるもの一部、"+", "?" および "*" は それに続くひとつの要素を繰り返します 。(要素とは、ひとつも文字、ひとつの文字セット、エスケープシーケンス、またはグループのことです。最後の 2 つについては後で説明します。要するに、正規表現の中にある任意のひとつのエンティティです) このような文字は、ワイルドカードまたは量指定子と呼ばれます。具体的に言うと、"?" は後ろに続く要素が 0 または 1 個のものにマッチし、"*" は後ろに続く要素が 0 個以上 のものにマッチし、"+" は後ろに続く要素が 1 個以上 のものにマッチします。いくつか例を示しましょう。正規表現 a? は "" (つまり空の文字列で、このケースでは特に便利ではありません) または "a" にマッチし、a* は "", "a", "aa", および a が連続して並んでいる限り何個でもマッチし、正規表現 a+ は "a", "aa", および a が連続して並んでいる限り何個でもマッチします (ただし空の文字列にはマッチしません)。セットについても同じことが言えます。正規表現 [0-9]+すべての整数 にマッチします。さてここで、読者が何を考えているか筆者にはわかります。そしてそれは間違っていません。もしこれを上記のケースでページ番号のマッチに使用したら、ひとつの正規表現だけですべてのページにマッチするのではないか? そのとおり、Page [0-9]+ of 423 は本の中のすべてのページにマッチするでしょう。

注釈

量指定子に関する注意点: 量指定子は通常、可能な限り多くのテキストとマッチさせようとします。ですから使用する場合には注意してください。これは "貪欲な動作" と呼ばれます。もちろん理由はおわかりですね。これは、たとえばタグをマッチさせようとする場合などに問題が起きます。たとえば、文字列 "<p class="calibre2">Title here</p>" から開きタグ (山カッコで囲まれた最初のパーツ、タグについては後述します) にマッチさせたいとしましょう。正規表現 <p.*> がそのタグにマッチすると思うかもしれませんが、実際には文字列全体にマッチしてしまいます。(文字 "." もまた特殊文字です。これは改行 以外 のすべての文字にマッチします。したがって、基本的には正規表現 .* はどのような内容でも 1 行すべてにマッチします) 代わりに <p.*?> を試してみましょう。これは量指定子 "*" を貪欲ではなくします。この正規表現は、意図したとりに最初のタグにだけマッチします。実はこれにはもうひとつ別のやり方があります。正規表現 <p[^>]*> も同様に最初の開きタグにマッチします。理由は次の章以降にわかるでしょう。ここでは、しばしば正規表現の書き方はひとつではないということを心に留めておいてください。

特殊文字ってすごいけど、ドットやクエスチョンマークにマッチさせたかったらどうすればいい?

もちろんできます。特殊文字の前にバックスラッシュをつければ、特別な意味のない文字リテラルとして解釈されます。特殊文字の前にバックスラッシュをつけた組み合わせのことを、エスケープシーケンスと呼び、特殊文字の前にバックスラッシュをつけることを文字をエスケープすると言います。エスケープシーケンスはひとつの要素と解釈されます。もちろんただ単に特殊文字をエスケープするだけでないエスケープシーケンスも存在します。たとえば "\t" はタブ文字を表します。エスケープシーケンスについては、また後で説明します。ちなみに、この特殊文字についてですが、この入門書の中で何か機能があると紹介する文字はどれも特殊文字であり、したがって文字リテラルとしたいときにはエスケープする必要があると考えてください。

一番便利なセットは何ですか?

聞かれると思ってました。便利なセットといえば、1 桁の数字にマッチする [0-9]、小文字 1 個にマッチする [a-z]、大文字 1 個にマッチする [A-Z]、アルファベット 1 文字にマッチする [a-zA-Z]、そして英数字 1 文字にマッチする [a-zA-Z0-9] などがあります。略記法としてエスケープシーケンスを使用することもできます:

\d

[0-9] と同等

\w

[a-zA-Z0-9_] と同等

\s

空白と同等

注釈

"空白" とは印刷されないものすべてを表す用語です。空白にはスペース、タブ文字、改行、改ページ、行頭復帰、ノーブレークスペースなどが含まれます。

注釈

検索で大文字と小文字を区別しない設定が有効になっていると、大文字と小文字のセットは大文字と小文字の両方にマッチします。そのような設定は、たとえば calibre 自身の Preferences->Searching や calibre 電子書籍ビューア の検索パネル、そして calibre 本の編集 ツールにあります。

セットに関する最後の注意点として、セットはある文字を 除く すべての文字と定義することが可能です。そのためには、文字 "^" をセットの 一番最初の文字として 指定します。したがって [^a] は a 以外のどの文字ともマッチします。これをセットの補完と呼びます。前述したエスケープシーケンスでの略記法にも補完できます。"\D" は数字以外のどの文字にもマッチし、[^0-9] と同等です。その他の略記法も、ご想像のとおり、それぞれ小文字の代わりに大文字を使うと補完になります。では、前の章で例として挙げた <p[^>]*> に戻ってみると、ここで使われている文字セットは山カッコを除く文字すべてにマッチさせようとしていることがわかります。

マッチさせたい文字列がいろいろあると、複雑になりますか?

心配いりません。まだまだ人生は楽勝です。次の例を考えてみましょう: 変換しようとしている本に、奇数ページごとに "タイトル" が、偶数ページごとに "著者名" が書かれているとします。印刷では見栄えがしますよね? しかし電子書籍では邪魔なだけです。表現全体を普通のカッコで囲むとグループ化できます。そして文字 "|" を使うと右側の表現 または 左側の表現の いずれか にマッチします。これをつなげれば、おしまいです。速すぎましたか? いいでしょう、まず最初に、奇数ページ用と偶数ページ用に表現をグループ化します。そうすると必要となる 2 つの表現として (タイトル)(著者名) が得られます。さて、これを縦線 ("|" は縦線と呼ばれます) を使って簡単にします。正規表現 (タイトル|著者名) を使うと、"タイトル" (奇数ページ) または "著者名" (偶数ページ) のいずれかにマッチするようになります。ほら、簡単でしょう?

もちろん縦線をグループ化するカッコなしで使用することも可能です。ところで量指定子はその前の要素を繰り返すと説明したときのことを覚えていますか? 縦線の作用は少し異なります。正規表現 "タイトル|著者名" は、上でグループを使った例とまったく同じように、文字列 "タイトル" または文字列 "著者名" のいずれかに一致します。縦線は、その前の表現全部と、その後ろの表現全部のうちから選択します。したがって、もし文字列 "Calibre" と "calibre" にマッチさせたくて、大文字と小文字の "c" だけを選択したいとすると、使用する正規表現は (c|C)alibre としなくてはなりません。こうすると "c" だけが選択されることが保証されます。もし c|Calibre とすると、文字列 "c" または文字列 "Calibre" にマッチするため、望んだ結果となりません。要するに、よくわからないときには縦線を使う場合にはグループ化しましょう。

見落としがあります...

…ちょっと待ってください。最後にひとつ、グループ化でできるすばらしいことがあります。前にマッチしたグループがあれば、そのグループをその正規表現の中で後で利用できるのです。グループには 1 から始まる番号がつけられ、参照したいグループの番号をエスケープすると参照できます。ですから 5 番目のグループを参照するには \5 とします。したがって文字列 "Test Test" に対して ([^ ]+) \1 で検索すると、文字列全体にマッチします。

最初の方で、正規表現に大文字と小文字を区別させない方法があると言いましたよね?

はい、そのとおりです。傾聴とご指摘をありがとうございます。フラグと呼ばれるものを使用すると、calibre にある物事をどのように処理して欲しいのか伝えることができます。フラグを正規表現に含めるには、特殊なコンストラクト (?ここにフラグ) を使用し、"ここにフラグ" の部分を指定したいフラグで置き換えます。大文字と小文字を区別しないようにするフラグは i なので、正規表現の中に (?i) を含めます。ですから (?i)test` は "Test", "tEst", "TEst" など思いつく限りすべてのバリエーションとマッチします。

もうひとつの便利なフラグ s を使用すると、ドットを改行を 含めて すべての文字にマッチさせることができます。正規表現の中で使用したいフラグが複数ある場合には、同じ記述の中に含めてかまいません。(?is) とすると大文字と小文字を区別せず、ドットがすべての文字にマッチします。どちらのフラグを先に記述しても同じです。(?si) は上のものと同等です。

だんだん正規表現がわかってきた気がします。calibre ではどう使いますか?

変換

変換の設定から始めましょう。非常によくできています。検索と置換 の部分に、変換中に置き換えたい文字列を記述する regexp (正規表現の略称) を入力できます。よくできている部分はウィザードです。ウィザードの杖をクリックすると、変換処理中に calibre に "見える" もののプレビューが表示されます。削除したい文字列のところまで下へスクロールして、選択してコピーし、ウィンドウの上部にある正規表現フィールドに貼り付けます。もしページ番号のような可変な部分があればセットと量指定子を使用して保護し、ついでに特殊文字があれば忘れずにエスケープします。テスト とラベルのついたボタンを押すと、calibre は正規表現で置換する部分をハイライト表示します。満足できたら OK を押して変換します。もし変換ソースに次の例のようなタグがある場合には、注意してください:

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

(図々しくも このスレッド からパクってきました) 一部のタグも削除する必要があります。この例では、タグ <b class="calibre2"> で開始することをお勧めします。次に、対応する終了タグ (開始タグ <tag> で、終了タグは </tag>) で終了する必要があります。このケースでは単純に次の </b> です。(これに関して不明な点があれば、優れた HTML マニュアルを参照するか、フォーラムで質問してください。) 開始タグは <b.*?> と表すことができ、終了タグは </b> で表せます。したがって <b.*?>.*?</b> を使用すると、このタグの間にあるものすべてを削除できます。しかしこの正規表現の使用はお勧めできません。なぜなら <b> タグで囲まれたもの (これは囲まれたテキストを太字にします) すべてを削除してしまうため、まず間違いなく本の一部が消えてしまうでしょう。代わりに囲まれた文字列の先頭部分も含めて、正規表現を <b.*?>\s*Generated\s+by\s+ABC\s+Amber\s+LIT.*?</b> とします。見えているとおりにスペースを含める代わりに、どのような文字列の組み合わせになっていてもキャッチできるよう量指定子つきの \s が使われています。新しい正規表現をテストするときには、削除したくないものが削除されることのないよう、calibre が何を削除するかを忘れずに確認してください。1 回の出現しか確認しないと、テキストの他の場所での食い違いを見逃す可能性があります。さらに、実際に望んだよりも多いまたは少ないタグを削除してしまうことがありますが、calibre は削除を行った後に破損したコードの修復を試みます。

本の追加

正規表現を利用できるもうひとつのものは、ファイル名からの書誌の抽出です。この機能は、設定の "本の追加" にあります。ここには特別な機能があります。書誌フィールドのフィールド名を使用できるのです。たとえば (?P<title>) は calibre が文字列のこの部分を本のタイトルとして使用することを示します。使用可能なフィールド名はウィンドウの下部に、使いやすいテストフィールドとともに一覧表示されています。例を挙げましょう。たとえば Classical Texts: The Divine Comedy by Dante Alighieri.mobi (言うまでもなくこの本はすでにライブラリの中にあります。何しろイタリアの詩の古典はみんな大好きですから) や Science Fiction epics: The Foundation Trilogy by Isaac Asimov.epub のような名前の大量のファイルをインポートしたいとします。この命名方法からは、明らかに calibre は意味のあるデータを抽出できません。書誌を抽出する標準の正規表現は (?P<title>.+) - (?P<author>[^_]+) ですから。このケースで機能する正規表現は [a-zA-Z]+: (?P<title>.+) by (?P<author>.+) です。ただし書誌フィールドのグループの内側では、そのフィールドが実際に何にマッチしているのかを表す表現を使う必要があります。そしてまた、calibre の提供するテストフィールドを使用する場合には、テストするファイル名のファイル拡張子を追加する必要があります。そうでないときちんと動く正規表現を使っていても、何もマッチが得られなくなります。

書誌を一括編集

最後の部分は、書誌フィールドで使われる正規表現 検索と置換 です。ライブラリで複数の本を選択してまとめて書誌編集を行うと、これにアクセスできます。この最後の機能を使用するにあたっては、特に十分に注意してください。非常におそろしいこと がライブラリに起きる可能性があります。テストフィールドを使って正規表現が思ったとおりに動作することを二重に確認し、本当に変更したい本だけを選択してください。正規表現検索モードでは、一つのフィールドで検索し、テキストを何かと置換し、さらにはその結果を別のフィールドに書き込むことさえ可能です。実用的な例を挙げてみましょう: たとえばライブラリに Frank Herbert の Dune シリーズがあり、Dune 1 - Dune, Dune 2 - Dune Messiah のような形で名前がついているとします。さてここで Dune をシリーズ名として取得したいとします。これを行うには、タイトルフィールドを (.*?) \d+ - .* で検索し、シリーズフィールドで \1 に置き換えます。何をしたかわかりますか? これはシリーズフィールドの置換に使われる、1 番目のグループへの参照です。これでシリーズをすべて設定できたので、タイトルフィールドでまた別の検索 .*? - を行い、"" (空の文字列) で置換し、再びそれをタイトルフィールドにすると、書誌がすっかりきれいになります。すごくありませんか? ところで、フィールド全体を置換する代わりに、フィールドの前または後ろに追加することも可能です。ですからもし本のタイトルの先頭にシリーズ情報を つけておきたい のであれば、そうすることも可能です。そして間違いなくすでにお気づきでしょうが、大文字と小文字を区別 とラベルのついたチェックボックスがあります。ですからここでは動作を選択するためのフラグを使用する必要がありません。

さて、これで正規表現のごく簡単な紹介は終わりです。願わくば紹介した内容が、少なくとも自分で学び続けることができるに足るものでありますように。 Python documentation for regexps から始めるとよいでしょう。

ただし最後にひとつだけ警告しておきます。正規表現は強力ですが、同時にまた非常に簡単に間違いを引き起こすものでもあります。calibre は正規表現が期待どおりに動くかどうか確認するために、すばらしいテストツールを用意しています。それを利用してください。自分の足を撃ち抜くような真似はしないでください。(この表現、すてきでしょう…) けれどももし、警告があったにもかかわらず足 (または身体のどこか他の部分) にケガを負ってしまった場合には、そこから学ぶよう努めてください。

クイックリファレンス

謝辞

記述や誤字修正などの助力に感謝します。

  • ldolse

  • kovidgoyal

  • chaley

  • dwanthny

  • kacir

  • Starson17

  • Orpheu

正規表現についてさらに詳しく学ぶには、 The Python User Manual を参照してください。calibre で使用している正規表現ライブラリは `regex <https://bitbucket.org/mrabarnett/mrab-regex/src/hg/>`_です。これは Python 標準のライブラリに比較するといくつか便利な拡張機能を備えています。