The Cusp of Helix

Intact Case

プログラム言語毎の正規表現の違い

Intact Case のライブラリでは、 camelCase と snake_case の変換に正規表現を使っています。 PHP と Ruby と Javascript で正規表現の仕様が異なり、それぞれのプログラミング言語用の変換パターンを作成する必要がありました。本記事内では、 Javascript は ECMAScript5 として記載しています( 2015年 7月現在)。

PatternKindPHPRubyJavascript
(?=)先読み言明
(?!)
(?<=)後読み言明none
(?<!)none
(?>)アトミックグループnone
??最短一致
*?
+?
?+最長一致none
*+none
++none

Javascript は「後読み言明」がないため、複雑なマッチパターンを表現するときに工夫が必要です。

正規表現のバグ

Intact Case のライブラリを実装している際、プログラミング言語によって同一のマッチパターンが期待通りの結果にならないケースがありました。調査した結果、マッチパターンの OR 条件の仕様が異なるのが原因だと分かりました。

以下の条件にマッチする正規表現で文字列変換を行った場合に、期待される結果を想定してみます。 Javascript には後読みがないので、 (?<=) を使わないマッチパターンを使います。

以下の場所に @ を追加する
  1. 文字列全体の先頭
  2. Abc の直後
マッチパターン/^|(Abc)/g
変換文字列"$1@"
対象文字列AbcAbcAbc
期待される結果@Abc@Abc@Abc@

コード

PHPpreg_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@