正規表現を書いて説明する
境界ケースと反例も一緒に出す
次の条件に一致する正規表現を書いてください:[要件を説明]。条件:1)正規表現を示す;2)各部分の意味を1つずつ説明する;3)一致すべき例を3つ、一致すべきでない例を3つ挙げる;4)どんな極端な入力でバックトラックが爆発するか、またそれをどう避けるかを示す。
使い方
一致すべきでない3つの例をそのまま貼って試しましょう。たいていそこで、欲張りすぎていることが分かります。
向いている場面
- 仕様が決まっている入力(注文番号、ログ行、固定形式の機器番号)を検証するとき。境界を列挙できます。
- 大量のログからフィールドを抽出するとき。目視で確認できない量が対象です。
向いていない場面
- JSON、HTML、その他の入れ子構造を解析するなら parser を使います。正規表現は入れ子の 1 段目で破綻します。
- 入力の形式自体が決まっていない(ユーザーが自由に書く住所)なら、まず形式を決めます。正規表現は曖昧さをエラーに変えるだけです。
例
入力
要件:中国本土の携帯番号に一致させる。+86 または 86 の接頭辞を許可。番号内の区切りとして空白とハイフンを許可。それ以外の文字は不可。 サンプル:+86 138 0013 8000、13800138000、8613800138000、12800138000(不一致)、+86 138-0013-8000
得られるもの
「正規化してから検証する」2 段構えを勧めます。接頭辞と区切りを除去したうえで `^1[3-9]\d{9}$` に照合し、区切りを巨大な 1 本の正規表現に織り込みません。そのうえで各部分を説明し、一致すべき例 3 つと一致すべきでない例 3 つを示します。最後に、`(\d+)+` のような書き方は長い数字列でバックトラックが爆発しますが、固定長の量指定子なら避けられることを述べます。よくある誤用
- HTML や入れ子の JSON を正規表現で解析すると、入れ子 1 段で破綻し、文書の半分を一致として返すこともあります。
- 一致すべき例だけを試し、一致すべきでない例を試さないと、`.*` でも動いているように見えます。
こう変えると効く
- 「バックトラックを使わない等価な書き方(RE2 や原子グループ)も示して」と加えると、悪意ある 1 行で CPU を占有されるのを防げます。
- 「この正規表現が明示的に対応しないケースを挙げて」と加えると、全入力を網羅しているという誤解を避けられます。