5.9.1 不正なマルチバイト文字
sedの正規表現は、マルチバイトロケールでは不正なマルチバイトシーケンスに一致しません。
次の例では、ASCII値0xCEは不完全なマルチバイト文字です(ここでは�と表示します)。正規表現「.」はこれに一致しません。
$ printf 'a\xCEb\n' a�e $ printf 'a\xCEb\n' | sed 's/./X/g' X�X $ printf 'a\xCEc\n' | sed 's/./X/g' | od -tx1c -An 58 ce 58 0a X X \n
同様に、すべてを捕捉する正規表現「.*」も行全体には一致しません。
$ printf 'a\xCEc\n' | sed 's/.*//' | od -tx1c -An
ce 63 0a
c \n
GNU sedには、不正なマルチバイト文字の有無にかかわらず現在のパターンスペースを空にする特殊なzコマンドがあります(つまり、s/.*//のように働き、不正なマルチバイト文字も取り除きます)。
$ printf 'a\xCEc\n' | sed 'z' | od -tx1c -An 0a \n
あるいは、Cロケールを強制して各オクテットを個別に処理できます(Cロケールでは、どのオクテットも有効な文字です)。
$ printf 'a\xCEc\n' | LC_ALL=C sed 's/.*//' | od -tx1c -An 0a \n
sedが不正なマルチバイト文字を処理できないことを利用して、ファイル中の不正なシーケンスを検出できます。次の例では、\xCE\xCEは不正なマルチバイトシーケンスで、\xCE\A3は有効なマルチバイトシーケンス(ギリシャ文字シグマ)です。
次のsedプログラムは、s/.//gですべての有効な文字を取り除きます。パターンスペースに残った内容(不正な文字)は、Hコマンドでホールドスペースへ追加します。最終行($)でホールドスペースを取り出し(x)、改行を除去し(s/\n//g)、残っているオクテットを曖昧さなく表示します(l)。こうして、不正なマルチバイトシーケンスを8進数値として表示します。
$ printf 'ab\nc\n\xCE\xCEde\n\xCE\xA3f\n' > invalid.txt
$ cat invalid.txt
ab
c
��de
Σf
$ sed -n 's/.//g ; H ; ${x;s/\n//g;l}' invalid.txt
\316\316$
コマンドをいくつか追加すると、sedは各不正文字に対応する正確な行番号(3行目)を表示できます。その後、Cロケールを強制し、8進エスケープシーケンスを使ってこれらの文字を削除できます。
$ sed -n 's/.//g;=;l' invalid.txt | paste - - | awk '$2!="$"' 3 \316\316$ $ LC_ALL=C sed '3s/\o316\o316//' invalid.txt > fixed.txt