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


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

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

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

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

<br>

## 先に結論

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

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

```
% 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` を展開してしまうため、エスケープや展開抑制の処理をする必要があるが、致し方ない。

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

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

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

[^3]: Python や PHP で `\\` と書くためには `r"\\"` や `'\\'` としなければならない

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

```
# ここでは 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] し、言語間でも
挙動が変る場合があると言うことだ。

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

<br>

## 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` の方が一塊と判断しやすく可読性に優れている。

<br>

### 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
```


<br>