1. ホーム
  2. regex

[解決済み】REエラー:Mac OS Xでの不正なバイトシーケンス

2022-02-12 19:03:55

質問内容

iOS へのクロスコンパイル用に、Mac OS X 上の Makefile の文字列を置き換えようとしています。この文字列には二重引用符が埋め込まれています。コマンドは以下の通りです。

sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure

そして、そのエラーは

sed: RE error: illegal byte sequence

ダブルクォート、カンマ、ダッシュ、コロンのエスケープを試しましたが、うまくいきません。例えば

sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure

デバッグにめちゃめちゃ苦労しているんです。どなたか sed を実行して、不正なバイト列の位置を表示させることができますか?あるいは、不正なバイト列が何であるかご存知の方はいらっしゃいますか?

解決方法を教えてください。

症状を示すコマンドの例です。 sed 's/./@/' <<<$'\xfc' が失敗するのは、バイト 0xfc は有効なUTF-8文字ではありません。
それに対して、注意してください。 GNU sed (Linux, macOSにもインストール可能)は、エラーを報告せずに、単に無効なバイトを通過させるだけです。

を使用することで 旧受理済み回答 は、本来のロケールのサポートが失われても構わないのであれば、選択肢のひとつです。 (米国のシステムで、外国文字を扱う必要がないのであれば、それでもいいかもしれません)。

しかし 同じ効果を得ることができます アドホック に対して 単一コマンド だけ :

LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure

注:重要なのは 有効 LC_CTYPE の設定 C ということで LC_CTYPE=C sed ... 通常 も動作しますが、もし LC_ALL が設定されている場合 C を上書きします。 LC_* -のようなカテゴリ変数があります。 LC_CTYPE . したがって、最も堅牢なアプローチは LC_ALL .

しかし、(効果的に)設定する LC_CTYPE から C は文字列を扱います 各バイトが独立した文字であるかのように ( いいえ はエンコーディング規則に基づいて解釈される)。 とは マルチバイトオンデマンドで UTF-8 エンコーディング は、OS X がデフォルトで採用している 外字 ある マルチバイトエンコーディング .

一言で言えば 設定 LC_CTYPE から C は、シェルとユーティリティが基本的な英字のみを文字として認識するようにします (7 ビット ASCII 範囲のもの)、そのため 外字は文字として扱われません。 そのため、例えば大文字/小文字の変換に失敗することがあります。

繰り返しになりますが、以下のようなことが必要ない場合は、この方法で問題ないでしょう。 一致 のようなマルチバイトエンコードされた文字があります。 é で、単に を通過させます。 .

これでは不十分な場合、および/または 原因究明 元のエラーの原因(どの入力バイトが問題を引き起こしたかの特定を含む)と エンコーディング変換を行う をオンデマンドで提供します。 読み込む の下にあります。


問題は、入力ファイルのエンコーディングがシェルのものと一致しないことです。
具体的には 入力ファイルにUTF-8で有効でない方法で符号化された文字が含まれている。 (@Klas Lindbäck がコメントで述べているように) - それが sed のエラーメッセージは、次のように伝えようとしています。 invalid byte sequence .

ほとんどの場合、入力ファイルでは シングルバイト8ビットエンコーディング のような ISO-8859-1 西ヨーロッパ言語のエンコードによく使われます。

アクセント記号付きの文字 à は、ユニコードのコードポイント 0xE0 (224) - と同じです。 ISO-8859-1 . しかし、その性質上 UTF-8 エンコーディングでは、この単一のコードポイントは、次のように表されます。 2 バイト 0xC3 0xA0 を渡そうとするのに対し シングルバイト 0xE0 無効 UTF-8の下で。

ここでは この問題のデモ という文字列を使って voilà としてエンコードされています。 ISO-8859-1 というように à として表現されます。 バイト(ANSI-Cで引用されたbashの文字列を介して( $'...' を使用する)。 \x{e0} でバイトを作成する)。

なお、この sed コマンドは事実上、単に入力を通過させるだけのノー・オプションですが、エラーを引き起こすために必要なのです。

  # -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'

簡単に言うと 無視 問題点 は、上記の LCTYPE=C というアプローチが可能です。

  # No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'

もし、あなたが 入力のどの部分が問題を引き起こしているかを判断する は、以下を試してみてください。

  # Convert bytes in the 8-bit range (high bit set) to hex. representation.
  # -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'

出力は、ハイビットが設定されているすべてのバイト(7ビットASCIIの範囲を超えるバイト)を16進数で表示します。(ただし、正しくエンコードされたUTF-8のマルチバイト列も含まれます。UTF-8で無効なバイトを特定するためには、より洗練されたアプローチが必要です)。


エンコーディング変換をオンデマンドで実行 :

標準ユーティリティ iconv に変換するために使用することができます ( -t ) および/または ( -f )エンコーディングになります。 iconv -l はサポートされているものをすべてリストアップしています。

FROMを変換する ISO-8859-1 を、シェルで有効なエンコーディングに変換します (ベースは LC_CTYPE であり、これは UTF-8 -をベースにしたもの)を、上記の例に基づいて作成します。

  # Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

なお、この を変換することで、外字に適切にマッチングさせることができます。 :

  # Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

入力されたBACKを変換するために ISO-8859-1 処理後、その結果をパイプで別の iconv コマンドを使用します。

sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1