0
0

正規表現におけるバックスラッシュ自身の表記方法について

Last updated at Posted at 2024-07-07

一部 pcregrep 1 を用いて説明しているが、これは言語仕様によるノイズを排除するためのもの

# STRING => 検索文字列
# REGEXP => 正規表現
% echo STRING | pcregrep 'REGEXP'

の形で説明に利用している。


先に結論

16 進数表記を用いて記載する。特にお勧めは、\xhh 形式(ブレース無し)の \x5c2

% echo \\ | pcregrep '\x5c'
\         # <= マッチしている

こちらで記載するクセを付けると正規表現中に埋没することなく、
「ああ、バックスラッシュも対象なんだな。」と一目で判断が付く。

# `[` か `\` か `]` に当るカスタム文字クラス
% echo \\ | pcregrep '[\[\x5c\]]'
\         # <= マッチしている

言語依存で、\x5c のバックスラッシュにエスケープが必要だとしても、
エスケープシーケンスは、\、文字列は \x5c と切り分ければ、可読性が確保出来る。

# `[` か `\` か `]` に当るカスタム文字クラス
% python -c 'import re; print(bool(re.match("[\[\\\\\]]", "\\")))'
True

#     => r"" を使わないと、\x5c の \ にエスケープが必要となる
% python -c 'import re; print(bool(re.match("[\[\\x5c\]]", "\\")))'
True

上記の通り、わかりやすい。

あとは、コメントに「\x5c は、 \ の 16 進数表記です。」と一言書いておけば、初見の人でも理解出来るはず。
一部言語では、宣言時に \x5c を展開してしまうため、エスケープや展開抑制の処理をする必要があるが、致し方ない。


以上、本稿の主張は終り。 気が向いたら下の文章も読んで下さい。

バックスラッシュを重ねることの問題点、というか発端

  1. 言語依存で、一文字エスケープするのに \\\\ と書かなければいけないものとかある
    ついでに言えば、 \\\ と書ける場合があったりする
  2. その言語を学習すれば、\\ と書ける様に出来る。すなわち、言語依存のため学習コストがかかる3 が、往々にして、その学習を怠るものだ
  3. (本稿では言及しないが)そう言うコードは、メタ文字以外も無意味にエスケープしまくっている
  4. そして、そのコードを渡されて、途方にくれる、、、

結論の所に例を書いたが、バックスラッシュのエスケープの数をちょっと変更すると、

# ここでは Python での例

# ◯ エスケープ 7個 +  \ 自身
#   [ か \ か ] に当たる
#       => 文字クラス内に \ 自身が2回含まれる形
% python -c 'import re; print(bool(re.match("[\[\\\\\\\\\]]", "\\")))'

# ◯ エスケープ 4個 +  \ 自身
#   [ か \ か ] に当たる
#       => 余った \ は? ケースバイケースで無視されるのか?
% python -c 'import re; print(bool(re.match("[\[\\\\\\]]", "\\")))'
True

# ◯ エスケープ 2個 +  \ 自身
#   [] か、\] に当たる(カスタム文字クラスの定義は "[\[\\\\]" の部分)
#      => 想定と違い、エスケープ 3個 +  \ 自身として処理される
% python -c 'import re; print(bool(re.match("[\[\\\\]]", "\\")))'
False

# ◯ エスケープ 1個 +  \ 自身
#   [] か、\] に当たる(カスタム文字クラスの定義は [\[\\\] の部分)
#      => 想定と違い、エスケープ 2個 +  \ 自身として処理される
% python -c 'import re; print(bool(re.match("[\[\\\]]", "\\")))'
False

# ◯ エスケープ 1個 
#  [ か、] に当たる
#       => 余った \ は? ケースバイケースで無視されるのか?
% python -c 'import re; print(bool(re.match("[\[\\]]", "\\")))'
False

こう言う風に動作してしまう。

問題は、意図的に書いたのか、間違えたのか判断出来ない場合がある4 し、言語間でも
挙動が変る場合があると言うことだ。


16進数表記を用いる

より、言語間で挙動の違いが少なくする為に。

既に結論で書いてはいるが、再度。

正規表現には Ascii コードを埋め込み検索する事が可能。

\ の 16 進数 Ascii コードは 5c、そして多くのスクリプト言語の実装では、\xhh の表記がサポートされている。
つまり、正規表現中に \x5c と書く

# `[` か `\` か `]` に当るカスタム文字クラス
% python -c 'import re; print(bool(re.match(r"[\[\x5c\]]", "\\")))'
True
% python -c 'import re; print(bool(re.match("[\[\\x5c\]]", "\\")))'
True

\\\\ よりは、\x5c の方が一塊と判断しやすく可読性に優れている。


PHP における \x5c\x{5c}

PHP では別の記法として \x{hh} が用意されている。で、これの挙動が一致しない。

% php -r  'print(preg_match("/\x5c/", "\\")) . "\n";'
PHP Warning:  preg_match(): No ending delimiter '/' found in Command line code on line 1

Warning: preg_match(): No ending delimiter '/' found in Command line code on line 1

% php -r  'print(preg_match("/\x{5c}/", "\\")) . "\n";'
1

どちらかの挙動に合せようとしなかったのだろうか。

これが同様に \x{hh} がサポートされている Perl だと一致している。

% perl -le 'print "\\" =~ /\x5c/;'
1
% perl -le 'print "\\" =~ /\x{5c}/;'
1

  1. PCRE の純正 grep。インストールの仕方などは省略

  2. (少なくとも調べた言語中では)移植性が高いため

  3. Python や PHP で \\ と書くためには r"\\"'\\' としなければならない

  4. 実際、エスケープ 4 個の例はエラーになる事を記載したものだが、動いてしまっている。謎だ

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0