「どこでも」動く正規表現
(johndcook.com)- 正規表現は実装ごとに対応機能や構文が異なるため、あるツールで通ったパターンが別の環境では 失敗したり修正 が必要になったりすることがある
- Perl のように機能が豊富な環境に慣れているほど互換性の問題に遭遇しやすく、インストール権限のないコンピューターまで考慮すると 共通部分集合 を使うほうが安全
- 「どこでも」を最も厳密に定義すると、リテラル、
[…]文字クラス、. * ^ $のような 基本的な特殊文字 しか残らないほど範囲が狭くなる - GNU
sed,awk,grepにsedとgrepの-Eオプションを併用すると活用できる共通機能が広がるが、この組み合わせではawkが概ね 最小公分母 になる - Emacs では
+? ( ) { } |にバックスラッシュが必要で、\\s,\\Sの意味も異なるため、同じ正規表現を複数のツールで使うには 例外的な構文 まで確認する必要がある
正規表現の互換性が難しい理由
- 正規表現の最大の不便さは 実装ごとの差異 から生じる
- あるツールでサポートされる機能が別のツールではまったく存在しないことがある
- 同じ機能でも構文が少しずつ異なる場合がある
- Perl は機能が豊富な正規表現環境なので、Perl では当然に見える機能が他の環境では欠けていることがある
- 他のツールの Perl 風代替品を使う方法もあるが標準的ではなく、同僚や顧客にそのまま実行できるコードを送りにくくなる
- ソフトウェアをインストールできないコンピューターで作業しなければならない状況まで考えると、複数環境で動作する 正規表現機能の部分集合 を見つけるアプローチが必要になる
- 「どこでも」の定義を厳しくするほど使える機能は減る
- リテラル
[…]文字クラス. * ^ $特殊文字
sed, awk, grep, Emacs における共通範囲
- 対象ツールを
sed,awk,grep, Emacs に絞ると、「どこでも」の基準を少し緩めにできる - GNU 版の
sed,awk,grepを使い、sedとgrepに-Eオプションを適用すると共通機能の一覧が広がる- 3 つのツールの正規表現機能は似ている
awkの機能は他のツールでも概ねサポートされている- 例外的に 単語境界 は
awkでは\\<,\\>を使い、\\b,\\Bとは異なる
- Emacs は
awkの機能の大半に対応するが、構文の違いがある+ ? ( ) { } |がawkと同じ役割を持つには、前にバックスラッシュが必要awkの\\s,\\Sに相当する表現は Emacs では\\s-,\\S-
- Emacs で
\\s,\\Sは空白/非空白ではなく 文字クラス を開始する-クラスは空白を意味する\\s.は句読点文字\\S.は非句読点文字
- この基準で使える機能は次のとおり
.^,$[…],[^…]*\\w,\\W,\\s,\\S\\1から\\9までの 後方参照\\b,\\B?,+|代替{n,m}繰り返し回数(...)キャプチャ
- ただし、
gawkは置換文字列では後方参照をサポートするが、正規表現自体では後方参照をサポートしない look-aroundは高度な機能と見なせ、\\dは数字に対する基本機能のように見えるが、多くの正規表現方言ではサポートされていない
1件のコメント
Hacker News のコメント
Emacs では何をエスケープすべきかをほとんど勘で当てる感じで、特に苦労します
rxという代替も一応ありますが[0]、実際に使って楽しいものではありません正規表現の構文そのものを超えて、実際の利用段階でもエンコーディングやエスケープの問題がよく起きます
シェルで正規表現を入力するなら適切にエスケープする必要があり、Python では raw string かどうかを確認する、といった具合です
それでも、ほとんどのツールで正規表現の使い方がある程度似た範囲に収まっているのは、現代の奇跡に近いです
[0]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Rx...
異なるエスケープ規則が重なり合う状況が延々と発生します
(と)が文字どおりにマッチするのは少し便利です筆者はあと少しでそこに到達しそうですが、結局 POSIX 基本正規表現はどこでも動くと言いたいように見えます
ただし Single Unix Specification 第8版に全員が追いついているわけではなく、その版で BRE が少し変わったという但し書きが付きます
そうした但し書きがなければ、そもそもその記事を書く必要もなかったはずです
以前、貪欲な意味論と leftmost maximal 意味論の両方で同じようにマッチする正規表現を見つける論文を書いたことがあります
https://par.nsf.gov/servlets/purl/10534654
どのツールがどの 正規表現言語を受け付けるのか、そして任意の部分文字列・接頭辞・接尾辞・文字列全体・1行・行内の部分文字列のうち何にマッチするのかを明確にすべきだと、いつも細かく見てきました
ここには [より広く使われているもの][1] があり、そのほかに PCRE と Python もあります
grep などで見かける古い形式の一部が [POSIX で仕様化されている][2] ことを知るまで、少し時間がかかりました
[1]: https://cppreference.com/cpp/regex#Regular_expression_gramma...
[2]: https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd...
Russ Cox の正規表現ページを共有したいです
読む価値のある良い資料だと思います
https://swtch.com/~rsc/regexp/
こうした理由から、RFC 9485 である I-Regexp: An Interoperable Regular Expression Format が重要です
https://datatracker.ietf.org/doc/html/rfc9485
Go 標準ライブラリの regexp パッケージは RE2 エンジンを使っているため、後方参照をサポートしていません
置換では使えますが、マッチングでは使えません
regexpは re2 を使っているのではなく、同じ概念を別途実装したものですルールエンジン、テンプレートエンジン、IFTTT 系エンジンで似たような挫折を経験したあと、JSONLogic 用の Rust ライブラリを作り、ほかの言語向けバインディングも使っています
https://github.com/GoPlasmatic/datalogic-rs
JSON Schema のドキュメントにも、推奨される 正規表現のサブセットがあります
https://json-schema.org/understanding-json-schema/reference/...