web-dev-qa-db-ja.com

trは「不正なバイトシーケンス」について文句を言う

私はUNIXを使い始めたばかりで、Kirk McElhearnの「Mac OS Xコマンドライン」を使っていくつかのコマンドを教えています。

通常のMS-Office Word文書でテキスト文字列を検索できるように、trgrepを使用しようとしています。

$ tr '\r' '\n' < target-file | grep search-string

しかし、それが返すすべては:

Illegal byte sequence.
robomechanoid:Position-Paper-Final-Draft robertjralph$ tr '\r' '\n' < Position-Paper-Final-Version.docx | grep DeCSS
tr: Illegal byte sequence
robomechanoid:Position-Paper-Final-Draft robertjralph$ 

viで作成したスクリプトで実際に同じ行を実行しましたが、検索は正しく行われます。

26
user74886

grepはテキスト処理ツールです。入力は テキストファイル であると想定しています。同じことがmacOSのtrにも当てはまるようです(trはバイナリファイルをサポートすることになっていますが)。

コンピュータは bytes のシーケンスとしてデータを保存します。テキストは一連の文字です。文字をバイトとしてエンコードする方法はいくつかあり、 文字エンコード と呼ばれます。ほとんどの世界、特にOSXでの事実上の標準文字エンコーディングは TF-8 で、これは nicode 文字セットのエンコーディングです。 256バイトしかありませんが、100万を超えるUnicode文字が使用できるため、ほとんどの文字は複数バイトとしてエンコードされます。 UTF-8は可変長エンコーディングです。文字に応じて、1バイトから4バイトで文字をエンコードできます。バイトのシーケンスの中には、UTF-8で文字を表さないものがあります。したがって、有効なUTF-8テキストファイルではないバイトのシーケンスがあります。

trは、このようなバイトシーケンスに遭遇したため、不満を言っています。 UTF-8でエンコードされたテキストファイルが表示されることを期待していますが、有効なUTF-8ではないバイナリデータが表示されています。

Microsoft Word文書はテキストファイルではなく、Word処理文書です。ワードプロセッシングドキュメントフォーマットは、テキストだけでなく、フォーマット、埋め込み画像などもエンコードします。ワードフォーマットは、ほとんどのワードプロセッシングフォーマットと同様、テキストファイルではありません。

locale を変更することで、バイトを操作するようにテキスト処理ツールに指示できます。具体的には、「C」ロケールを選択します。これは、基本的に「何も気にしない」という意味です。コマンドラインで、ロケール設定を 環境変数 で選択できます。

export LC_CTYPE=C
tr '\r' '\n' < target-file | grep search-string

エラーは発生しませんが、target-fileは指定したほとんどの検索文字列が含まれている可能性が低いバイナリファイルであるため、何も役に立ちません。

ちなみに、Mac OS 9以前から残っているテキストファイルがない限り、tr '\r' '\n'はあまり便利なコマンドではありません。 \r(改行)は、Mac OS Xより前のMac OSでは改行区切り文字でした。OSX以降、改行区切り文字は\n(改行、UNIX標準)で、テキストファイルには改行が含まれていません。 Windowsは、2文字のシーケンスCR-LFを使用して改行を表します。 tr -d '\r'は、WindowsテキストファイルをUnix/Linux/OSXテキストファイルに変換します。

では、コマンドラインからWord文書を検索するにはどうすればよいでしょうか。 .docx Word文書は、実際にはいくつかのファイルを含む Zipアーカイブ であり、主なファイルは [〜#〜] xml [〜#〜] にあります。

unzip -l Position-Paper-Final-Version.docx

Mac OS Xには、Zipファイル内を検索するための zipgrep ユーティリティが含まれています。

zipgrep DeCSS Position-Paper-Final-Version.docx

Docx形式のXMLファイルは主に1つの巨大な行で構成されているため、結果はあまり読みやすくありません。ドキュメントの本文テキスト内を検索する場合は、アーカイブからファイルWord/document.xmlを抽出します。このファイルには、ドキュメントのテキストに加えて、ドキュメントの構造を表すXMLマークアップが含まれていることに注意してください。 sed を使用してXMLマークアップを少しマッサージすると、扱いやすい行に分割できます。

unzip -p Position-Paper-Final-Version.docx Word/document.xml |
sed -e 's/></>\n</g' |
grep DeCSS

ロケールからのチャーマップはUTF-8であるため、バイナリファイルで問題が発生すると思います。 Cロケールに切り替えるだけです。

LC_ALL=C tr '\r' '\n' < target-file | LC_ALL=C grep search-string
13
vinc17