正規表現の使用例のチートシートを作りたい話
きっかけ
おぼろげながら浮かんできたんです、「正規表現」という言葉が。
けど、正規表現の基本をおさらいするくらいであれば幾らでも情報はでてくるので、もうちょっと具体的な使い方を書いてみようと思いました。
正規表現の実践例
先読み(ともしかしたら後読み)を使っています。基本正規表現と呼ばれる正規表現では使用できない可能性が高いことに注意してください。
jsで使用可能であることを確認しています。
整数
- 0
- 1 とそれに 1 ずつ加えて得られる自然数
- これらに−1を乗じて得られる負数
らしいっす。以上、wikipediaより。
一応今回は
- -0は不許可としました。
- 010のような先頭が0で始まる非0も不許可としました。
/^(-?[1-9]?(?!0)[0-9]+|0)$/.test('-1')
実数(小数)
国によって.と,の違いがあったりしますが、
今回は
- 小数点は.としました。
- .で始まる値は許可しました。
- -.で始まる値は悩みましたが、不許可としました。
- .00000000のような、小数点以下が0で終わる値は許可としました。
簡単に説明
- 小数点以下が無い場合、小数点以下しか無い場合が共に考えられる為、どちらも存在しない場合を先に潰しています
- 整数部で-0を許可しなかった為、引き続きただの「-0」を潰しています
- 上記まとめて(?!(-0)?$)辺りでも動きますが、個人的には例外ケースに関しては列挙で良い気がしてます
- -0.0のようなケースに対応するため、先頭が-0で始まるケースを小数点の判断の直前で行っています
/^(?!$)(?!-0$)((?:-?[1-9]?(?!0)[0-9]+|0)?(?:(^-0)?\.[0-9]+)?)$/.test('.00001')
IPアドレス
IPアドレスと聞くと0-255のドット区切りのイメージはあるんですけど、RFC791を流し見しても書いてる気がしなかったので非公式ながら、
- (0~255).(0~255).(0~255).(0~255)
- (0~255).(0~255).(0~255).(0~255)/(1~32)
のいずれかを満たす書式をここではIPアドレスと呼びます。
単なる32ビットの16進数表現を二桁毎に区切ってそれを10進数表現にしたもの、くらいの認識です。/以降はマスクですね。
マスクの適用範囲にビットを立てるのは厳密には正しいと言えるか悩ましいですが、今回は無視します。
まず、バリデーションであればこう書けそう。
0~255の表現が一度しか出てこないのがポイントです。
「[0-9][0-9]」は「[0-9]{2,2}」でも良いですが、この構成なら並べた方が個人的には見通しが良いです。
また「?:」は必須じゃないです。
/^((?:(?!^\.)(?:^|\.)(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){4,4}(?:\/([1-2][0-9]|3[0-2]|[1-9]))?)$/.test('192.168.0.1')
抽出的な事がしたい場合は、先頭が数字だった場合(1192.168.0.1みたいな状況)に含めるか含めないか悩ましいですが、ここでは一旦含めない方向で考えます。
全体マッチだと最初の一文字が含まれるケースがある為、IP抽出は部分マッチを確認する必要があります。したがって「?:」はあった方が取り扱いし易いです。
'xxx192.168.0.1xxx'.match(/(?:[^\d]|^)((?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3,3})(?:[^\d]|$)/)
メールアドレス
RFC5322があるわけですが、ぶっちゃけてしまうとこの通りに扱っているサーバーを探すのが大変です。例えば、RFC5322に準拠するとおそらく日本語のドメインなども弾くことになってしまいますが、弾くべきメールアドレスなのかどうかは実際には判断付きません。
携帯のメールアドレスのドットの扱いなどもかつては有名でした。(今はフューチャーフォン利用者が殆どいないので忘れられた記憶かもしれません)
したがって、少なくともバリデーションは
- @(アットマーク)が一つ以上あること
- (DB等に保持するのであれば)文字数のチェック(512文字も格納できれば十分過ぎると思います。RFC5321)
くらいで済ませて、そのメールアドレスに実際に存在確認メールを送信するのがベターです。
/@/.test('nobokko+zenn.dev@example.com')
余談ですが、RFC2606にてexample.comは予約されています。
囲み文字列
"こういうの"
適切な呼び方が思い浮かびませんでした。
今回は
- 囲む文字は"(ダブルクォーテーション)とします。
- 囲む文字を中で使いたい場合は\(バックスラッシュ)+"
- その他エスケープ全般は\+1文字を使用する
という事にします。エスケープ文字が決まっている場合は「.」の代わりにそれらを並べると良い感じになるのではないでしょうか。
// \x5C = バックスラッシュ
'<a href="http://example.com/xxx/yyy/zzz" target="_blank">link</a>'.match(/"((?:[^\x5C"]|\x5C.)*)"/)
まとめ
必要に迫られていないので、事例の方が思いつきませんでした。
正規表現自体は結構いい感じに出来た気がします。
お疲れさまでした。
参考
可視化できるサイトが面白いです。
Discussion