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