正規表現のイロハ

Advent Calendar 2023　3日目ということで12月3日、イチ・ニ・サンですね。日本語ならばイロハだろう、ということで正規表現のイロハについて書いておこうと思います。

# 正規表現とは
正規表現は正規な表現のしかた、ということになりますが[Wikipedia](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)
> 正規表現（せいきひょうげん、英: regular expression）によると、文字列の集合を一つの文字列で表現する方法の一つである。

ということだそうです。なんかわかるようなわからんような。
何十年も前の情報工学的な知識によりますと、

https://zenn.dev/yucatio/articles/0b554e59db0158

> つまり、正規表現とは、"文字列の集合を一つの文字列で表現する方法のうち強い制約がかかったもの"と言えそうです。

ということのようですね。
制約をかけた表現でいろんなことに対応できるようにしよう、といったものだと思えば良さそうです。

# 検索に正規表現を使う

正規表現が一番活用できるのは**検索**でしょう。
検索するときにいろんな条件を1つの文で作ることができます。

正規表現でどんな文字が使えるかは、利用するツールによって違いますので、
自分が使いたいツール、プログラミング言語とかで調べていく必要があります。
UNIXで使われるものであれば先述の[Wikipedia](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE)にも載ってます。

単純な利用方法としては、「あ」か「い」か「う」が含まれる部分を見つけたいよ、となれば
`[あいう]`
と書けばよいですし、それが何文字か続く文が検索したいなら
`[あいう]{3,3}` （「あ」か「い」か「う」の文字が3つ続く部分を検索）
とかやるか、
`[あいう]+`とやって1文字以上で探す、とかができます。

普通に文字列検索するときに「あああ」「ああい」「ああう」「あいあ」「あいい」「あいう」みたいに総当たりで検索しなくても1文で1回で検索できる。便利ですね！

# 置換に正規表現を使う

検索で使うだけではなく、さらに置換にも利用できるのも素敵なところです。
※ちなみに「正規表現」という意味では、情報工学的に全然正規じゃないのでイレギュラーな機能のようです。

検索した部分を`()`でくくって、それにマッチした部分を \1 とか $1とかで指定できてしまいます。
たとえば「今夜は眠い」という文字を「やっぱり今夜は眠い」に変えたいなーと思ったとき、「今夜」で検索して「やっぱり今夜」に置換する、という方法もありますが
`(今夜)`を`やっぱり$1`で置換する、ということができます。
単純なキーワードの置換であればどちらも変わりませんが、1行単位で置換を行ったりする場面に遭遇すると、こういう機能が非常にありがたく感じますよね。

# さらに複雑な検索：先読みも使う

正規表現による検索は基本的にOR検索になります。「あ」か「い」か「う」がある文字を検索する、ということですが、「あ」も「い」も「う」もある文を検索したい、となると先読みが便利です。
`(?=調べたい正規表現)(?=ほかの調べたい正規表現)`みたいにすると1行の中で「調べたい正規表現」でマッチするか確認して、さらに「ほかの調べたい正規表現」でマッチするか確認する、どちらもマッチする部分を示す、といったことが可能です。

先読みもあれば後読みもあります。肯定的もあれば否定的もあります（先述の表現は「肯定的先読み」）
詳しくは

https://qiita.com/shotets/items/98f3828b6e5f08d42498

とかを参照してみてください。

# 試してみる

Visual Studio Codeでも試せる方法として、[ことのはたんご](https://plum-chloride.jp/kotonoha-tango/index.html)で練習すると参考になるかなと思いやってみます。
なぜ「ことのはたんご」かというと、出題される単語が公開されているからです。

https://note.com/plumchloride/n/n8d25cad96348

[GitHubにテキストデータとして](https://github.com/plumchloride/tango/blob/main/kotonoha-tango/public/data/A_data_new.csv)用意されてます。4万7千行とかありますがダウンロード可能です。

実際に試しても良いですが、例題でやってみましょう。ファイルをVisual Studio Codeで開いて、標準の検索で試します。
ちなみにVisual Studio Codeで正規表現を使うときは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553291/ad89025e-2e07-2840-51d7-3c92599a3797.png)
Alt+Rで有効にすれば良いようです。

例題は「**カレンダー**」にしましょうか。

答えを知らないとしてやってみます。
私はとりあえず小さい文字とか長音とかで検索してます。
`シャチョウ`
だと1文字も検出されません
`ニュートン`
だと「ー」「ン」がヒットするけど場所が違う、となりますね。
そこから正規表現で検索してみます。
`[^シャチョウニュト][^シャチョウニュト][^ーシャチョウニュト][^シャチョウニュト][^ンシャチョウニュト]`
とするとヒットしなかった文字「シャチョウニュト」以外（`^`を使うと否定になります）の文字を検索します。場所違いなので、場所が違っていたところもハズれになるので3文字目の「ー」5文字目の「ン」も検索対象から外しています。

ただ、これだけで検索すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553291/95caac74-a1af-b19f-474f-2c8e095819d0.png)
1万5千行とかヒットしてしまいますね。

そこで先読みを利用します。

`(?=[^シャチョウニュト][^シャチョウニュト][^ーシャチョウニュト][^シャチョウニュト][^ンシャチョウニュト])(?=.*ー.*)(?=.*ン.*)`
と、まずは除外する文字で調べ、そのうえで「ー」「ン」のある行を探す、となると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553291/3ecb1cf4-25df-aaae-2e46-bd1c09e268df.png)

5百行とグッと候補が減りました！

※ちなみに私は別途プログラムを作っていて、対象行を抜き出して引数に与えるとどの文字が一番多く使われているかを調べられるようにしています。さらに検索の量を減らしてますが、本題からはズレるので特に説明しません:relaxed:

候補の中で
`レンタカー`
あたりで調べてみたことにします。
すると「レ」「ン」「カ」「－」が検出され、「レ」「ン」「カ」は場所違い、「－」は正解と表示されることになりますね。

`(?=[^レタシャチョウニュト][^ンタシャチョウニュト][^ターシャチョウニュト][^カシャチョウニュト]ー)(?=.*ン.*)(?=.*カ.*)`

で検索してみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553291/379fc8fc-19cc-287d-ea84-5f490f7d6979.png)

残り4行です！

ことのはたんごは10回まで試行できるので、総当たりでやっても間に合います。
まあ、4行のうち多く使われる文字が「ダ」なので、「カレンダー」か「ピカンダー」で検索するのが妥当かなと。とりあえず「カレンダー」で検索して正解！となるなら5回で成功となります。まず上出来でしょう。

といった感じで、1行で多様な検索に対応できる正規表現。是非活用していければと思います！