34. 付録-正規表現使いになろう

 筆者はプログラマとしては10年遅れていると自負がありますが、そのいくつかの技術はいまでも現役です。

 今回のテーマは「小説執筆に『正規表現』」を生かそうという内容です。


 一般的な小説では「文末は。で終わる」みたいな規則があります。それに従って書き進めるわけですが、人間はAIちゃんと違い、こういうところで規則を完全に守るのを苦手としています。

 機械=コンピューターが得意なことは、機械的にチェックしようというのが、今回の話です。


 この手のエッセイは他にもあります。ちょっとなろう内で検索したところ以下の先行研究、メモが見つかりました。

 こちらで紹介されているものは、そのまま再掲しないので、こちらも目を通すといいかもしれません。


 n7581bk 正規表現メモ 卯田手石:作


 ここでは主に「文字列検索」について書いていきます。

 普通の検索では文字列と同じものしか検索できないですよね。正規表現では検索文字列の代わりに「検索パターン」というものを指定します。

 これは一種の「式」のようなものになります。


 この正規表現による検索、そしてエディタに付属している「Grep」系機能を利用すると、フォーマットチェックや、特定の動詞や表現を探すなど色々な応用ができるようになります。


 一番シンプルなパターン例:

 (A) /abcde/


 これは単純に文字列の「abcde」を検索するものです。

 正規表現はいくつかのプログラミング言語で/と/で囲むという規則があるので、例ではそれに倣っています。

 多くのテキストエディタでは正規表現オプションを有効にすることで、/と/の記述はしないのがお約束なので、その辺は読み替えてください。

 また、Microsoft Wordをお使いの人もいると思います。Wordには非常に似た機能に「ワイルドカード」があり、表記が若干違うものの、ほぼ同じです。

 ここでは筆者はサクラエディタ遣いなのでそれ風で紹介します。Meryなどはほぼ同じ表記だと思います。

 ただし、Meryでは+や*などで検索した場合に、同じ文字が複数回ヒットします。これだとF3などで検索しつつ移動してみるときに不便だと思うので、その辺は好みでエディタを選んでください。

 サクラもMeryもOnigumo(またはOniguruma)という正規表現エンジンです。

 サクラが使っているbregonigはPerl風、MeryのonigはRuby風という違いもあるようです。



 式なのでいくつかの表記を紹介します。


 選択:

 (B) /(オレンジ|アップル)ジュース/


 これは「オレンジジュース」か「アップルジュース」が検索にヒットします。一致するのをマッチするといいます。

 括弧は数学の括弧と同じで、式の文字の優先順位を指定するのに使います。こまかい優先順位は筆者も分かっていないので、うまくいかないときはとりあえず括弧で囲っています。


 集合:

 (C) /[A-GS]ランク/


 これは「Gランク」から「Bランク」「Aランク」そして「Sランク」にマッチします。

 /[a-z]/ と書くと、aからzの文字コード順の範囲の文字がマッチします。

 /[^a-z]/ のように最初に^を書くと、その指定した集合「以外」の文字にマッチするようになります。否定形です。

 /[abc] のように書くと、aまたはbまたはcのいずれかという風になります。書いた文字のいずれか一文字にマッチします。

 しかしこれではSSSランクの最初のSSにマッチしません。


 (D) /(SSS|[A-GS])ランク/


 これで「SSSランク」にもマッチするようになります。


 文字列のパターンを指定するので、繰り返しも表現できます。


 (E) /S+ランク/


 +記号は、1回以上の繰り返しです。これは{1,}と同じです。

 これで「Sランク」「SSランク」はてまて「SSSSSSSSランク」とかにもマッチするようになります。

 このような回数を指定するのを「量指定子」といいます。

 量指定子は繰り返したいパターンのすぐ後ろに書きます。


 ? 0回または1回

 * 0回以上

 + 1回以上

 {6} ちょうど6回

 {1,3} 1回以上、3回以下

 {2,} 2回以上

 {,4} 0回以上、4回以下


 こんな感じになります。

 何でもいいから一文字っていう場合には「.」を使います。

 何でもいいから適当な長さの文字列の場合には「.*」や「.+」というのをよく使います。

 これはワイルドカードでいう「*」と同じです。ただ「文字の種類」「繰り返し回数」が分離していてそれぞれ指定する必要があるということです。


 正規表現には「最長一致の原則」というものがあり、より長い文字列にマッチしようとする性質があります。

 もしより短いほうにマッチしたいみたいな場合には、量指定子の後ろに?を付加すると、最短一致になります。

 これは例えば /<a href=".+">/ という風にHTMLタグにマッチさせようとしたときに「.+」には当然後ろの「">」も含まれますから「<a href="1.html">1</a> <a href="2.html">2</a>」のようになっていると、最長一致では2の終わりまで一致してしまいます。しかし最初に出てきたほうを採用したいので、/<a href=".+?">/ という風に書きます。


 ちょっと特殊な指定もあります。

 それは「行頭」と「行末」を指定する方法です。

 これは先頭の文字ではなく、文字と文字の間の部分、仮想の文字にマッチします。

 なお、各行の行頭と行末にマッチさせるには正規表現のオプションで「マルチラインモード」を指定する必要があります。たぶんほとんどのエディタでは既定でONになっています。

 シングルラインモードでは、^は文章の先頭、$は文末にマッチします。


 ^ 行頭

 $ 行末


 (F) /^[^ 「『(【≪《〈〔\r\n]/


 例えば(F)は行頭で1文字目が全角空白または括弧類以外にヒットします。

 []の中の^は範囲以外の意味、[]の外に書いた^は行頭を意味します。

 つまり行頭に普通の文字が来ているものを探せます。

 一般的な書き方をした小説では、これで、字下げができてない行を探し当てることができます。

 なかにはステータス表示や、サブタイトル行、システムメッセージみたいなものがヒットする場合もあります。


 改行文字は\nで探せるのが普通ですが、サクラエディタでは\r\nを使います。

 またサクラエディタでは改行をまたいだ検索ができないので「/\n /」という風な改行直後の文字を前の改行コードで探すことができません。


 (G) /^[  \t]+$/


 これで空白行のうち、スペース、全角空白などのゴミが入った行を探せます。

 \tというのはタブ文字です。

 置換後に空文字列「」を入れて全置換するとゴミ掃除ができます。


 (H) /[  \t]+$/


 (G)の行頭指定を消せば、行末に余分についている空白文字を探せます。

 空白行もヒットしますが、まぁそこはご愛嬌ということで。

 こちらも置換後に空文字列「」を入れて全置換するとゴミ掃除ができます。


 (I) /^[ ]{2,}/


 行頭が2つ以上、下がってしまっている行を探すことも簡単です。

 ここでは全角空白のみ検索していますが、半角空白なども指定すれば、変な行を見つけるのに役に立ちます。


 (J) /^[  \t]+[「『(【≪《〈〔<]/


 行頭括弧の前が、下がってしまっているものを探せます。

 地の文の行頭に括弧の文が埋まっていて、字下げしている行もヒットしてしまうので、目視で確認したりするといいです。


 (K) /^[  \t]+[「『(【≪《〈〔<].*[「」』)】≫》〉〕>」]$/


 ちょっと改造して(J)にさらに「括弧で終わっている行」をくっつけて検索すれば、地の文の行頭問題は一応解決します。これは行全体がマッチします。

 なにやら括弧のお化けみたいですが、基本は/行頭[空白]+[括弧開始]任意の文字列*[括弧終了]行末/ となっているだけです。


 使い方は様々です。

 例えば「おもう」という単語をチェックして漢字に書き換えたいとします。

 しかし動詞なので「おもった」とか「おもいます」とか活用します。

 「思う」は「ワ行五段活用」なので「わいうえおっ」と変化します。


 (L) /おも[わいうえおっ]/


 これで、だいたいの精度で「おもう」という動詞を探せます。


 (M) /(おも|思も|思|想も|想)[わいうえおっ]/


 漢字表記を含めてピックアップしたい場合には、このような感じにします。

 「思もう」と書く人はあまりいませんが、一応探せます。

 参考として活用表を載せておきます。


 カ行 かきくけこい

 カ行 かきくけこっ(行く)

 ガ行 がぎぐげごい

 サ行 さしすせそ_

 タ行 たちつてとっ

 ナ行 なにぬねのん

 バ行 ばびぶべぼん

 マ行 まみむめもん

 ラ行 らりるれろんっ

 ラ行 らりるれろいっ(ナサル)

 ワ行 わいうえおっ

 ワ行 わいうえお_(問う、請う)


 さまざまな範囲の例を挙げておきます。

 範囲の表記は、文字コード順で指定します。現在主なエディタなどはUnicodeになっているので、Unicodeコードポイントで指定する必要があります。

 Shift_JISでは「JIS第1水準漢字」などを簡単に表すことができたのですが、それができなくなっています。

 MS-IMEであれば「IMEパッド」というのを使えば文字一覧順で見ることもできます。ただしこのソフトすごい重いので、Webサイトとかで確認したほうがいいかもしれないです。


 「+」をつければ連続した文字が一度にヒットします。単語単位みたいな感じです。

 1文字ずつ探したい場合は、末尾の「+」を消してください。


 ASCII(スペース以外):

 (N) /[!-~]+/


 ASCIIのうち数字:

 (O) /[0-9]+/


 ASCIIのうちアルファベット:

 (P-1) /[a-zA-Z]+/

 (P-2) /[a-z]+/i  または (P-3) /[A-Z]+/i


 「大文字小文字の同一視」のオプションがあります。ここでは/iで表していますが、エディタオプションに読み替えてください。

 間違って「[A-z]+/i」のように指定すると間の記号[\]^_`などにもマッチします。

 形式番号とかいろいろある場合は /[a-zA-Z0-9_\-]/ のように記号を追加するのもいいでしょう。

 []の中に-を文字として指定したい場合は、\-と円マーク(バックスラッシュ)でエスケープします。

 通常のパターン中に[]や()*+?などを文字として使いたいときも\(や\[という風に書きます。


 カタカナ:

 (Q) /[ァ-ヺ][ァ-ヺー゛゜]*/


 ちょっと変な感じですが、これでだいたいはカバーできます。

 伸ばし棒や濁点はひらがなにも使うので、カタカナの直後のみ有効にしてあります。

 半角カタカナ、変体仮名とか発音用の特殊なものには未対応です。


 カタカナ(Onigumo系):

 (R) /\p{Katakana}+/


 Onigumo/bregonigで有効な書式です。

 \p{●}でユニコードのプロパティを参照します。

 これでもカタカナの文字は選択できますが「伸ばし棒」「濁点」には対応していません。これはUnicode文字種に基づくからです。


 カタカナ文字列(Onigumo系):

 (S) /\p{Katakana}[\p{Katakana}ー゛゜]*/


 これでいい気がしないでもないですが、汎用性は低いです。


 全角ASCIIのうちアルファベットと数字:

 (T) /[a-zA-Z0-9]/


 これらの文字だけなら簡単です。

 記号類が若干厄介で、連続していませんので、個別対応が必要です。

 また<>()などの記号は小説には普通に使うので除外するなどの対応が必要になります。


 全角ASCII(一部記号以外):

 (U) /[a-zA-Z0-9‘’”#$%&*+,-./:;=@^_ ̄¥]/


 ()<>!?[]{}|を除いてあります。

 必要な記号は追加してください。

 場合によっては、半角ASCIIに正規化するといい文字です。全角に正規化する人もいます。


 漢字(Onigumo系):

 (V) /\p{Han}+/


 簡単ですね。Unicodeでは漢字は中国語読み「ハンジ」なのでHan、統合漢字をUniHanと呼びます。

 もちろん、Onigumo系限定です。

 JavaScriptの新しい規格、ES2018、ECMAScript 2018では、同じような機能が実装されて「/\p{sc=Han}/u」で指定することができます。

 これで例えば「●●感」「喪失感」「期待感」のようなものを一括して調べたい場合などに使えます。あとはあまり具体的な応用が思いつきませんが、まぁ覚えておくと使うことがあるかもしれません。

 漢字以外を探したい場合は、\Pと大文字で書けばOKです。


 ●●感:

 (W) /\p{Han}{2}感/


 漢字以外:

 (X) /\P{Han}/



 漢字を表す古い正規表現(Shift_JIS/CP932/Bregexpなど):

 (Y) /[亜-黑]/


 (Y)のタイプの正規表現はUnicodeベースではまともに機能しないので注意してください。


 漢数字:

 (Z) /[〇一二三四五六七八九十百千万億兆][、,〇一二三四五六七八九十百千万億兆]*/


 普通に並べるぐらいしか方法はありません。

 しかも普通の単語中の「十分」みたいな表記もヒットしてしまっていまいちです。

 二文字以上(最後の*を+にする)で調べればそれなりのほしい結果になるかもしれません。


 ルビ候補(Onigumo系):

 (AA) /[\p{Han}a-zA-Za-zA-Z&&[^〇々]]{1,10}([\((《][\x{3041}-\x{30FA}ー…・ ]{1,20}[\))》])/


 なろうルビ振りはわりと複雑で正確ではないのですが、とりあえずこれでいいことにします。

 ルビ指定の|を記入したものを除外する方法が思いつきませんでした。

 あと括弧の種類が前後で違うとルビになりませんが(AA)ではヒットします。

 これで検索して思わぬ場所がヒットしたらそこはルビになっているかもしれません。

 &&[^〇々]というのは「〇々」を除外するという意味です。Onigumo系などの独自表記だと思います。

 「〇」は漢数字のゼロですがなろうルビでは漢字扱いされません。


 ルビの前後の括弧の種類が間違っていてルビ失敗を探す:

 (AB-1) /\([^()()《》\r\n]{1,20})|([^()()《》\r\n]{1,20}\)/

 (AB-2) /[\((][^()()《》\r\n]{1,20}》《[^()()《》\r\n]{1,20}[\))]/


 また括弧のお化けみたいですが、全角括弧で始まっているものと半角括弧で終わっているものと、その反対を探しているだけです。


 常用漢字、人名漢字、JIS第1水準、JIS第2水準などの表以内一覧を調べるような正規表現は、書けないことはないですが、事実上漢字をただ並べることになります。




 正規表現の置換では置換後に置換前の文字列を指定することができます。


 (AC-置換前) /[||]([^\r\n《》||]{1,10})《([^\r\n]{1,20})》/

 (AC-置換後) [[rb:$1 > $2]]


 これで、|と《》を使ったルビをpixivのルビに置き換えたりもできます。

 自動ルビや()のルビには対応していませんが、式を書き換えればある程度対応できます。

 置換後に「$&」を指定すると、置換前全体を表します。「$1」から順番にキャプチャといって、()で囲った中の文字列を指定することができます。


 (AD-置換前) /([0-9]+)ゴールド/

 (AD-置換後) $1G


 単位として使われている「ゴールド」を執筆中に気が変わって「G」表記に一気にしたい場合などは、これでだいたい変換できます。


 (AE-置換前) /([0-90-9〇一二三四五六七八九十百千万億兆]+)ゴールド/


 漢字などにも対応したいならこんな感じでしょう。

 そのまま「/ゴールド/」では「ゴールドのリング」とかにもヒットしてしまい、置換すると意味不明になってしまいます。



 いかがでしょうか。ちょっと意味不明で分からないかもしれません。

 正規表現に関するサイトはいっぱいあるので、検索してみてください。

 ただし、方言が多いので注意してください。

 どんな文字列でも完全に指定できるわけではないというところも、奥が深いです。

 ある程度使いこなせるようになると、応用の幅が広い技術です。

 参考にしてください。

  • Xで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

新規登録で充実の読書を

マイページ
読書の状況から作品を自動で分類して簡単に管理できる
小説の未読話数がひと目でわかり前回の続きから読める
フォローしたユーザーの活動を追える
通知
小説の更新や作者の新作の情報を受け取れる
閲覧履歴
以前読んだ小説が一覧で見つけやすい
新規ユーザー登録無料

アカウントをお持ちの方はログイン

カクヨムで可能な読書体験をくわしく知る