正規表現で少し詰まったので、記事としてアウトプットしておく。
Ruby 2.7.6で検証しています。
やりたいこと
(空白か先頭)特定の文字列(空白か文末)という正規表現を書きたい。改行は入らないので考慮を今回はせず1行の文字列で考えます。
この正規表現を特定の文字列strに対して、reg(str)
としましょう。
具体的には
> ' 大阪市'.match?(`reg('大阪市')`)
=> true
> ' 大阪市'.match?(`reg('大阪')`)
=> false
> ' 大阪 市'.match?(`reg('大阪')`)
=> true
のようなイメージ。純粋にinclude?関数を使うと、後者はtrueになってしまうので別途正規表現を作る必要が出てきました。
こんな正規表現を書いた
> ' 大阪市'.match?(/[\s|\A]#{'大阪市'}[\s|\z]/)
=> false
> ' 大阪市'.match?(/[\s|\A]#{'大阪'}[\s|\z]/)
=> false
通常の文字(a
とかあ
とか)であれば[]
を使えばいいのですが、[]
は文字クラスであり、\A
はアンカー(文字ではない。位置にマッチする表現)ので動きません。
グループを使おう
今回やりたい正規表現は下記の通りでした。
> ' 大阪市'.match?(/(\A|\s)#{'大阪市'}(\s|\z)/)
=> true
> ' 大阪市'.match?(/(\A|\s)#{'大阪'}(\s|\z)/)
=> false
空白と先頭のグループにマッチするかを判定、直後に特定の文字列があるかを確認して、更にその直後に空白と文末のグループにマッチするかを見る、という表現になりました。
もう一歩踏み込んで
グループに?:をつけることで、キャプチャせずにグループ化を行うことができ、若干早くなるみたいです。
> ' 大阪市'.match?(/(?:\A|\s)#{'大阪市'}(?:\s|\z)/)
=> true
> ' 大阪市'.match?(/(?:\A|\s)#{'大阪'}(?:\s|\z)/)
=> false
更に、上記だと全角スペースや非ASCIIの空白文字の対応が漏れてしまっているので、\s
を[[:space:]]
にして
> ' 大阪市'.match?(/(?:\A|[[:space:]])#{'大阪市'}(?:[[:space:]]|\z)/)
=> true
> ' 大阪市'.match?(/(?:\A|[[:space:]])#{'大阪'}(?:[[:space:]]|\z)/)
=> false
とするのが良さそうです(@scivolaさんありがとうございました!)。