簡潔な答え
file
はファイルのエンコーディングを推測するだけであり、間違っている可能性があります (特に、特殊文字が大きなファイルの後半にしか現れない場合)。
- を使用
hexdump
して、非 7 ビット ASCII テキストのバイトを調べ、一般的なエンコーディング (ISO 8859-*、UTF-8) のコード テーブルと比較して、エンコーディングが何であるかを自分で判断できます。
iconv
ファイルの内容に関係なく、指定した入出力エンコーディングを使用します。間違った入力エンコーディングを指定すると、出力が文字化けします。
- を実行した後でも
iconv
、エンコーディングを推測するfile
方法が限られているため、変更が報告されない場合があります。file
具体的な例については、私の長い回答を参照してください。
- 7 ビット ASCII (別名 US ASCII) は、バイト レベルでは UTF-8 および 8 ビット ASCII 拡張 (ISO 8859-*) と同一です。したがって、ファイルに 7 ビット文字しかない場合は、UTF-8、ISO 8859-*、または US ASCII と呼ぶことができます。これは、バイト レベルではすべて同一であるためです。ファイルに 7 ビット ASCII 範囲外の文字が含まれている場合にのみ、UTF-8 やその他のエンコーディング (このコンテキストで) について話すのが理にかなっています。
長い答え
今日これに遭遇し、あなたの質問に出くわしました。おそらく、この問題に遭遇した他の人々を助けるために、もう少し情報を追加できます.
アスキー
第 1 に、ASCII という用語はオーバーロードされているため、混乱を招きます。
7 ビット ASCII には 128 文字 (10 進数で 00 から 7F または 0 から 127) しか含まれていません。7 ビット ASCII は、US-ASCII と呼ばれることもあります。
アスキー
UTF-8
UTF-8 エンコーディングは、最初の 128 文字に 7 ビット ASCII と同じエンコーディングを使用します。したがって、最初の 128 文字の範囲内の文字のみを含むテキスト ファイルは、UTF-8 または 7 ビット ASCII でエンコードされているかどうかにかかわらず、バイト レベルで同一になります。
コードページのレイアウト
ISO 8859-* およびその他の ASCII 拡張
拡張 ASCII (または高 ASCII )という用語は、標準の 7 ビット ASCII 文字と追加の文字を含む 8 ビット以上の文字エンコーディングを指します。
拡張アスキー
ISO 8859-1 (別名 "ISO Latin 1") は、西ヨーロッパのほとんどの文字をカバーする特定の 8 ビット ASCII 拡張規格です。東ヨーロッパ言語およびキリル言語用の ISO 規格は他にもあります。ISO 8859-1 には、ドイツ語とスペイン語の Ö、é、ñ、ß などの文字のエンコードが含まれています (UTF-8 はこれらの文字もサポートしていますが、基本的なエンコードは異なります)。
「拡張」とは、ISO 8859-1 が 7 ビットの ASCII 標準を含み、8 番目のビットを使用して文字を追加することを意味します。したがって、最初の 128 文字については、ISO 8859-1 はバイト レベルで ASCII および UTF-8 エンコード ファイルの両方と同等です。ただし、最初の 128 文字を超える文字を扱い始めると、バイト レベルでは UTF-8 と同等ではなくなり、「拡張 ASCII」でエンコードされたファイルを UTF-8 でエンコードする場合は、変換を行う必要があります。
ISO 8859 および独自の適応
エンコーディングの検出file
今日学んだことの 1 つfile
は、ファイルの文字エンコーディングを常に正しく解釈できるとは限らないということです。
ファイル (コマンド)
このコマンドは、ファイルが何であるかではなく、ファイルがどのように見えるかのみを示します (ファイルがコンテンツを参照する場合)。内容が一致しないマジック ナンバーをファイルに挿入することにより、プログラムをだますのは簡単です。したがって、このコマンドは、特定の状況以外ではセキュリティ ツールとして使用できません。
file
タイプを示唆するファイル内のマジックナンバーを探しますが、これらは間違っている可能性があり、正確さの保証はありません。file
また、ファイル内のバイトを調べて、文字エンコーディングを推測しようとします。基本的file
に、ファイルの種類とエンコードを推測するのに役立つ一連のテストがあります。
私のファイルは大きな CSV ファイルです。file
は、このファイルを US ASCII エンコードとして報告しますが、これはWRONGです。
$ ls -lh
total 850832
-rw-r--r-- 1 mattp staff 415M Mar 14 16:38 source-file
$ file -b --mime-type source-file
text/plain
$ file -b --mime-encoding source-file
us-ascii
ファイルにウムラウト (つまり Ö) が含まれています。最初の非 7 ビット ascii は、ファイルに 10 万行を超えるまで表示されません。file
これが、ファイルのエンコーディングが US-ASCII ではないことに気付かない理由だと思います。
$ pcregrep -no '[^\x00-\x7F]' source-file | head -n1
102321:�
私は Mac を使用しているので、PCRE の grep
. GNU grep では、-P
オプションを使用できます。別の方法として Mac では、GNU grep を取得するために( Homebrewなどを介して) coreutilsをインストールすることもできます。
私は のソース コードを掘り下げていませんfile
。また、man ページではテキスト エンコーディングの検出について詳しく説明してfile
いませんが、エンコーディングを推測する前にファイル全体を調べていないと推測しています。
私のファイルのエンコーディングが何であれ、これらの非 7 ビット ASCII 文字は問題を引き起こします。ドイツ語の CSV ファイルが で;
区切られており、単一の列を抽出しても機能しません。
$ cut -d";" -f1 source-file > tmp
cut: stdin: Illegal byte sequence
$ wc -l *
3081673 source-file
102320 tmp
3183993 total
cut
エラーと、私の「tmp」ファイルには 102321 行に最初の特殊文字がある 102320 行しかないことに注意してください。
これらの非 ASCII 文字がどのようにエンコードされるかを見てみましょう。最初の非 7 ビット ascii を にダンプhexdump
し、少し書式設定を行い、改行 ( 0a
) を削除して、最初の数行だけを取ります。
$ pcregrep -o '[^\x00-\x7F]' source-file | head -n1 | hexdump -v -e '1/1 "%02x\n"'
d6
0a
別の方法。最初の非 7 ビット ASCII 文字が 102321 行の 85 番目の位置にあることはわかっています。その行を取得して、hexdump
85 番目の位置から始まる 2 バイトを取るように指示します。文字は「.」で表され、次のバイトは「M」です...したがって、これはシングルバイト文字エンコーディングです。
$ tail -n +102321 source-file | head -n1 | hexdump -C -s85 -n2
00000055 d6 4d |.M|
00000057
どちらの場合も、特殊文字は で表されd6
ます。この文字はドイツ語の Ö であるため、ISO 8859-1 に含める必要があると推測しています。案の定、「d6」が一致することがわかります ( ISO/IEC 8859-1 )。
重要な質問... ファイルのエンコーディングを確認せずに、この文字が Ö であることをどのように知ることができますか? 答えは文脈です。私はファイルを開き、テキストを読み、それが何の文字であるかを判断しました。Vimで開くと、Ö として表示されます。これは、Vim が文字エンコーディング (この場合)を推測file
するよりも優れているためです。
したがって、私のファイルは ISO 8859-1 のようです。理論的には、残りの非 7 ビット ASCII 文字をチェックして、ISO 8859-1 が適切であることを確認する必要があります...ファイルをディスクに書き込むときに、プログラムが単一のエンコーディングのみを使用するように強制するものは何もありません。 (マナー以外)。
チェックをスキップして、変換ステップに進みます。
$ iconv -f iso-8859-1 -t utf8 source-file > output-file
$ file -b --mime-encoding output-file
us-ascii
うーん。file
変換後も、このファイルは US ASCII であることがわかります。で再度確認してみましょうhexdump
。
$ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2
00000055 c3 96 |..|
00000057
間違いなく変化です。2 バイトの非 7 ビット ASCII (右側の「.」で表される) があり、2 バイトの 16 進コードはc3 96
. 調べてみると、現在は UTF-8 になっているようです (c3 96
は UTF-8 でのエンコーディングですÖ
) UTF-8 エンコーディング テーブルと Unicode 文字
しかし、file
まだファイルをus-ascii
? file
これは、ファイル全体を見ないという点と、最初の非 7 ビット ASCII 文字がファイルの最後まで出現しないという事実に戻ると思います。
sed
ファイルの先頭に Ö を付けて、何が起こるか見てみましょう。
$ sed '1s/^/Ö\'$'\n/' source-file > test-file
$ head -n1 test-file
Ö
$ head -n1 test-file | hexdump -C
00000000 c3 96 0a |...|
00000003
かっこいい、ウムラウトがあります。ただし、エンコーディングはc3 96
(UTF-8) であることに注意してください。うーん。
同じファイル内の他のウムラウトを再度確認します。
$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055 d6 4d |.M|
00000057
ISO 8859-1。おっとっと!エンコーディングを台無しにするのがいかに簡単かを示しています。明確にするために、UTF-8 と ISO 8859-1 エンコーディングを同じファイルに混在させることができました。
先頭にウムラウト (Ö) を付けてマングルされた (混合エンコーディング) テスト ファイルを変換して、何が起こるか見てみましょう。
$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000 c3 83 c2 96 0a |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055 c3 96 |..|
00000057
UTF-8 であった最初のウムラウトは、ISO 8859-1 として解釈されましたiconv
。これは、私たちが望んでいるものではなく、iconf に指示したことだからです。d6
2 番目のウムラウトは、 (ISO 8859-1) からc3 96
(UTF-8)に正しく変換されます。
もう一度やり直しますが、今回は の代わりに Vim を使用して Ö の挿入を行いますsed
。Vim は (「latin1」別名 ISO 8859-1 として) 以前はエンコーディングをより適切に検出しているように見えたので、一貫したエンコーディングで新しい Ö を挿入する可能性があります。
$ vim source-file
$ head -n1 test-file-2
�
$ head -n1 test-file-2 | hexdump -C
00000000 d6 0d 0a |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055 d6 4d |.M|
00000057
実際、vim は、ファイルの先頭に文字を挿入するときに、正しい/一貫した ISO エンコーディングを使用しました。
ここでのテスト: file は、ファイルの先頭にある特殊文字を使用したエンコーディングをより適切に認識しますか?
$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8
はい、そうです!この話の教訓。file
常に正しいエンコーディングを推測することを信用しないでください。同じファイル内でエンコーディングを混在させるのは簡単です。疑問がある場合は、ヘックスを見てください。
file
大きなファイルを扱うときのこの特定の制限に対処するハックは、ファイルを短くして、特殊な (ASCII 以外の) 文字がファイルの早い段階で表示されるようfile
にすることで、それらを見つけやすくします。
$ first_special=$(pcregrep -o1 -n '()[^\x00-\x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1
次に、(おそらく正しい)検出されたエンコーディングを入力としてフィードしてiconv
、正しく変換していることを確認できます。
アップデート
[Christos Zoulas]は、参照さfile
れるバイト数を構成可能にするように更新しました。機能要求に対する 1 日でのターンアラウンド、すばらしい!
http://bugs.gw.com/view.php?id=533
コマンド ラインから解析済みファイルから読み取るバイト数を変更できるようにする
この機能はfile
バージョン 5.26 でリリースされました。
エンコーディングについて推測する前に、より大きなファイルを調べるには時間がかかります。ただし、より良い推測が追加の時間と I/O を上回る可能性がある特定のユースケースのオプションがあると便利です。
次のオプションを使用します。
−P, −−parameter name=value
Set various parameter limits.
Name Default Explanation
bytes 1048576 max number of bytes to read from file
何かのようなもの...
file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check
file
...推測する前にファイル全体を強制的に調べたい場合は、うまくいくはずです。もちろん、これはfile
5.26 以降を使用している場合にのみ機能します。
file
US-ASCII の代わりに UTF-8 を強制的に表示する
file
他の回答のいくつかは、ファイルにプレーンな 7 ビット ascii しか含まれていない場合でも、UTF-8 を表示しようとすることに焦点を当てているようです。これを熟考すれば、おそらくこれをやりたくないはずです。
- ファイルに 7 ビットの ascii しか含まれていないのに
file
、ファイルが UTF-8 であるとコマンドが示している場合、そのファイルには UTF-8 固有のエンコーディングの文字が含まれていることを意味します。それが本当ではない場合、混乱や問題が発生する可能性があります。file
ファイルに 7 ビットの ASCII 文字しか含まれていないときに UTF-8 で表示される場合、これはプログラムのバグですfile
。
- UTF-8 形式の入力ファイルを必要とするソフトウェアは、バイト レベルでは UTF-8 と同じであるため、プレーンな 7 ビット ascii の消費に問題はありません。ファイルを入力として受け入れる前にコマンド出力を使用しているソフトウェアがあり、
file
UTF-8を「認識」しない限りファイルを処理しない場合...まあ、それはかなり悪い設計です。これはそのプログラムのバグだと思います。
プレーンな 7 ビット ascii ファイルを取得して UTF-8 に変換する必要がある場合は、その文字の UTF-8 エンコーディングを使用してファイルに単一の非 7 ビット ascii 文字を挿入するだけで完了です。しかし、これを行う必要があるユースケースは想像できません。これに使用する最も簡単な UTF-8 文字は、バイト オーダー マーク ( BOM ) です。これは、ファイルが非 ASCII であることを示唆する特別な非印刷文字です。通常は無視されるため、ファイルの内容に視覚的な影響を与えるべきではないため、これがおそらく最良の選択です。
Microsoft のコンパイラとインタープリター、およびメモ帳などの Microsoft Windows 上の多くのソフトウェアは、ヒューリスティックを使用するのではなく、BOM を必要なマジック ナンバーとして扱います。これらのツールは、テキストを UTF-8 として保存するときに BOM を追加し、BOM が存在するか、ファイルに ASCII のみが含まれていない限り、UTF-8 を解釈できません。
これが重要です:
またはファイルにASCIIのみが含まれています
そのため、Windows の一部のツールでは、BOM 文字が存在しない限り、UTF-8 ファイルの読み取りに問題があります。ただし、これはプレーンな 7 ビット ascii のみのファイルには影響しません。つまり、これは、BOM 文字を追加してプレーンな 7 ビット ascii ファイルを強制的に UTF-8 にする理由にはなりません。
ここでは、BOM を不要なときに使用することの潜在的な落とし穴について詳しく説明します (一部の Microsoft アプリで使用される実際の UTF-8 ファイルには BOM が必要です)。 https://stackoverflow.com/a/13398447/3616686
それにもかかわらず、それでもやりたい場合は、ユースケースを聞いてみたいと思います. 方法は次のとおりです。UTF-8 では、BOM は 16 進シーケンスで表される0xEF,0xBB,0xBF
ため、この文字を単純な 7 ビット ascii ファイルの先頭に簡単に追加できます。非 7 ビット ascii 文字をファイルに追加すると、ファイルは 7 ビット ascii だけではなくなります。元の 7 ビット ASCII コンテンツをまったく変更または変換していないことに注意してください。ファイルの先頭に 1 つの非 7 ビット ASCII 文字を追加したため、ファイル全体が 7 ビット ASCII 文字で構成されなくなりました。
$ printf '\xEF\xBB\xBF' > bom.txt # put a UTF-8 BOM char in new file
$ file bom.txt
bom.txt: UTF-8 Unicode text, with no line terminators
$ file plain-ascii.txt # our pure 7-bit ascii file
plain-ascii.txt: ASCII text
$ cat bom.txt plain-ascii.txt > plain-ascii-with-utf8-bom.txt # put them together into one new file with the BOM first
$ file plain-ascii-with-utf8-bom.txt
plain-ascii-with-utf8-bom.txt: UTF-8 Unicode (with BOM) text