シェルスクリプトで正規表現の照合シンボル[.string.]と等価クラス[=char=]と文字クラス[:classname:]を正しく使う方法

## はじめに

POSIX で標準化されている正規表現の 照合シンボル `[.string.]`、等価クラス `[=char=]`、文字クラス `[:classname:]` の正しい意味と使い方です。検証は主に Debian 上で行っています。シェルスクリプト（コマンド）で動作確認していますが、本質的にはロケールの話なので各プログラミング言語にも当てはまるのではないかと思います。

**参考記事** [シェルスクリプトの正規表現の詳細解説（令和最新版）](https://qiita.com/ko1nksm/items/53abc144558b9bb5629f)

## 注意 これらの表記が使えないと思ったら

**これらはすべてブラケット表現 `[]` の中で使う特殊な文字を意味する表記です**。念の為ですがブラケットが 2 重になっているわけではありません。`[]` の中に`[:digit:]` 等を書いているだけです。だから `[a[:digit:]z]` のように前後に別の文字を入れることもできます。

また、商用 Unix （Solaris 10/11 等）では互換性のために POSIX に準拠してない歴史的な方のコマンドがデフォルトで使われている場合があり、 これらの表記に対応していないので **POSIX 準拠モード**にしてから使用してください。（参考 [「え？UNIXなのにPOSIXに準拠してないの！？」シェルスクリプト実行環境をPOSIX準拠モードに変更して互換性を上げる方法 (Solaris, AIX, HP-UX, Linux, macOS, BSD)](https://qiita.com/ko1nksm/items/6ebdcba7050da2f3c585)）

**歴史的なコマンド（POSIX 非準拠）での解釈** （主に商用 Unix のデフォルト、以下は Solaris 11 の例）

```sh
# これは [] に囲まれた [:digit: と ] （期待したとおりに動かない）
$ echo "g]" | /usr/bin/sed "s/[[:digit:]]/@/g"
@

# これは [] に囲まれた :digit: （期待したとおりに動かない）
$ echo "abcdefg" | /usr/bin/sed "s/[:digit:]/@/g"
abc@ef@

# POSIX に準拠している方なら期待したとおりに動く
$ echo "a123b" | /usr/xpg4/bin/sed "s/[[:digit:]]/@/g"
a@@@b
```

POSIX に準拠している環境であれば動かないことはないと思いますが、もしこの記事に書いた通りにやっても動かない環境があれば教えて下さい。（繰り返しますが、商用 Unix では POSIX 準拠モードにするのを忘れないこと）

## 照合シンボル (collating symbol) [.string.]

一部の言語（ロケール）には、複数文字で 1 文字を意味する文字（「[合字](https://ja.wikipedia.org/wiki/%E5%90%88%E5%AD%97)」「[二重音字](https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%87%8D%E9%9F%B3%E5%AD%97)」等？）があるようで、それにマッチさせるためのものです。照合シンボルはロケール毎に定義されているものなので、どんな文字列でも指定できるわけではありません。（つまり勝手に作った `[.abcde.]` のような指定ができるわけではない）

参考

- [Regex collating symbols](https://stackoverflow.com/questions/35042202/regex-collating-symbols)
- [ウェールズ語の正書法](https://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%83%BC%E3%83%AB%E3%82%BA%E8%AA%9E%E3%81%AE%E6%AD%A3%E6%9B%B8%E6%B3%95)

```sh
$ echo "a cch chc z" | LC_COLLATE=cy_GB.utf8 sed 's/[a[.ch.]z]/@/g'
@ c@ @c @
```

実行して「無効な照合文字です」とか「Invalid collation character」とか「invalid collating element」とかいうエラーが出る場合は、存在しない照合シンボルを指定しているか、適切なロケールに設定されていないからです。`locale -a` で指定したロケールがインストールされていることを確認してください。なお Debian でロケールの追加は `dpkg-reconfigure locales` を使います。

このような感じで、使える照合シンボルが定義されています。

```sh
$ cat /usr/share/i18n/locales/cy_GB | grep collating
collating-symbol  <ch-digraph>
collating-element <ch> from "ch"
collating-element <cH> from "cH"
collating-element <Ch> from "Ch"
collating-element <CH> from "CH"
collating-symbol  <dd-digraph>
collating-element <dd> from "dd"
collating-element <dD> from "dD"
...
```

## 等価クラス (equivalence class) [=char=]

アクセント記号がついていてもついてなくてもマッチします。アクセント記号を同一文字とみなすかどうかはロケール毎に定義されているものなので、すべてのロケールで機能するものではありません。

```sh
$ echo 1e2è3é4ê5ë6 | LC_COLLATE=en_US.UTF-8 sed 's/[a[=e=]z]/@/g'
1@2@3@4@5@6
```

正しくマッチしないのは適切なロケールに設定されていないからです。`locale -a` で指定したロケールがインストールされていることを確認してください。

## 文字クラス (character classes) [:classname:]

POSIX 文字（キャラクタ）クラスとマッチします。

**注意** POSIX の正規表現では `[ ]` を「ブラケット表現」、`[:name:]` を「文字クラス」と定義していますが、他の言語などでは `[ ]` が「文字クラス」（`[:name:]` は「POSIX 文字クラス」）と定義されている場合があるので注意が必要です。（おそらく POSIX の定義が後から作られたもの）

```sh
$ echo "abc123def" | sed "s/[a[:digit:]z]/@/g"
@bc@@@def
```

以下の文字クラス名はすべてのロケールで使用可能です。

`[:alnum:]`, `[:cntrl:]`, `[:lower:]`, `[:space:]`, `[:alpha:]`, `[:digit:]`, `[:print:]`, `[:upper:]`, `[:blank:]`, `[:graph:]`, `[:punct:]`, `[:xdigit:]`

追加で `[:name:]` でロケール固有の文字クラスが使えそうなんですが `sed` でも `grep` でも使えませんでした。

[POSIX - Regular Expressions](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap09.html) より

> In addition, character class expressions of the form:
> 
> [:name:]
> 
> are recognized in those locales where the name keyword has been given a charclass definition in the LC_CTYPE category.

以下は長い行を折り返しています。

```sh
$ locale -k LC_CTYPE | grep ctype-class-names
ctype-class-names="upper";"lower";"alpha";"digit";"xdigit";"space";"print";
"graph";"blank";"cntrl";"punct";"alnum";"combining";"combining_level3";
"jspace";"jhira";"jkata";"jkanji";"jdigit"
```

`/usr/share/i18n/locales/ja_JP` にも書いてあるので `[:jhira:]` とか使えてもよさそうなんですが？