質問: 平仮名を判定しようと、`[あ-ゞ]`という正規表現を書いたのですが、`sed`(GNU sed)では使えませんでした。 (この正規表現は間違っています。**平仮名の判定に真似しないで下さい**。sayuriさんの回答を参照のこと) ``` echo "abcあいう123" |sed -re 's/^[あ-ゞ]+//g' # エラーになる: sed: -e expression #1, char 16: Invalid collation character ``` GNU系の`awk`や`grep`(`-P`オプション付き)では使えるようなのですが、`sed`はどうも駄目なようです。 `[あ-ゞ]`のような正規表現の代替になる手段や、これがそのまま使える`sed`コマンドなどを探しています。 (なぜ使えないのか、という疑問もあります) --- Ubuntu16.04で、`en_US.UTF-8` と `ja_JP.UTF-8` のロケールでテストしています。 GNU sed は PCRE(libpcre.so.3)にリンクされている物でテストしていますが、`libc`のみの物でも再現しました。 `locale charmap`の実行結果はテストしている`en_US.UTF-8`と`ja_JP.UTF-8`の環境では両方とも、`UTF-8`になります。
回答: 環境変数`LC_COLLATE`に`C.UTF-8`を指定する事で解決しました。 ``` echo "abcあいう123" |LC_COLLATE=C.UTF-8 sed -re 's/[ぁ-ゞ]/X/g' #=> abcXXX123 ``` `sed`だけでなく、(`-P`オプション無しの GNU) `grep` や`bash`にも有効で、日本語の文字範囲を使えるようになりました。 ``` echo "abcあいう123" |LC_COLLATE=C.UTF-8 grep -oE '[ぁ-ゞ]+' #=> あいう LC_COLLATE=C.UTF-8 bash -c '[[ abcあいう123 =~ [ぁ-ゞ]+ ]]; echo $BASH_REMATCH' #=> あいう ``` * `LC_ALL`が設定されていると、`LC_COLLATE`だけ変えても効果がない * `LC_CTYPE`と`LC_COLLATE`に互換性の無い値を指定すると、動作は未定義になる という注意点があります。 ### `en_US.UTF-8` や `ja_JP.UTF-8` でエラーになった理由 ユニコードでは平仮名は連続しており、当然に正規表現の文字範囲でも連続しているものとして扱われると思っていたのですが、そうではありませんでした。 ロケールの`LC_COLLATE`グループでは文字の順序が定められており、この順序は正規表現でも使用されます。 そして、Ubuntu16.04のロケール定義(`/usr/share/i18n/locales/en_US`、`/usr/share/i18n/locales/ja_JP`)では`LC_COLLATE`で平仮名が連続していませんでした。 `en_US`には平仮名が含まれていません。 `ja_JP`には平仮名がありますが、連続しているのは「ぁ」から「ん」までで、「ゔ」はそもそも含まれておらず、「ゞ」などは離れた位置にありました。 sayuriさんの回答のように、指定する文字によって動いたりエラーの種類が変わったりするのは、ここらへんの事情が原因のようです。 `C.UTF-8`の定義は平仮名を全て含んでおり、文字コード順に並んでいるので、期待通りに動いています。