プログラム言語毎の正規表現の違い
Intact Case のライブラリでは、 camelCase と snake_case の変換に正規表現を使っています。 PHP と Ruby と Javascript で正規表現の仕様が異なり、それぞれのプログラミング言語用の変換パターンを作成する必要がありました。本記事内では、 Javascript は ECMAScript5 として記載しています( 2015年 7月現在)。
Pattern | Kind | PHP | Ruby | Javascript |
---|---|---|---|---|
(?=) | 先読み言明 | ✔ | ✔ | ✔ |
(?!) | ✔ | ✔ | ✔ | |
(?<=) | 後読み言明 | ✔ | ✔ | none |
(?<!) | ✔ | ✔ | none | |
(?>) | アトミックグループ | ✔ | ✔ | none |
?? | 最短一致 | ✔ | ✔ | ✔ |
*? | ✔ | ✔ | ✔ | |
+? | ✔ | ✔ | ✔ | |
?+ | 最長一致 | ✔ | ✔ | none |
*+ | ✔ | ✔ | none | |
++ | ✔ | ✔ | none |
Javascript は「後読み言明」がないため、複雑なマッチパターンを表現するときに工夫が必要です。
正規表現のバグ
Intact Case のライブラリを実装している際、プログラミング言語によって同一のマッチパターンが期待通りの結果にならないケースがありました。調査した結果、マッチパターンの OR 条件の仕様が異なるのが原因だと分かりました。
以下の条件にマッチする正規表現で文字列変換を行った場合に、期待される結果を想定してみます。 Javascript には後読みがないので、 (?<=) を使わないマッチパターンを使います。
以下の場所に @ を追加する
- 文字列全体の先頭
- Abc の直後
マッチパターン | /^|(Abc)/g |
---|---|
変換文字列 | "$1@" |
対象文字列 | AbcAbcAbc |
期待される結果 | @Abc@Abc@Abc@ |
コード
PHP | preg_replace('/^|(Abc)/', '$1@', 'AbcAbcAbc'); |
---|---|
Javascript | "AbcAbcAbc".replace(/^|(Abc)/g, "$1@"); |
Ruby | 'AbcAbcAbc'.gsub(/^|(Abc)/) { "#{$1}@" } |
プログラミング言語毎の実行結果です。 Javascript と Ruby は、期待通りの変換結果が得られませんでした。
PHP | @Abc@Abc@Abc@ |
---|---|
Javascript | @AbcAbc@Abc@ |
Ruby | @AbcAbc@Abc@ |