68

一連のファイルを US-ASCII から UTF-8 にトランスコードしようとしています。

そのために、私はiconvを使用しています:

iconv -f US-ASCII -t UTF-8 file.php > file-utf8.php

元のファイルは US-ASCII でエンコードされているため、変換が行われません。どうやらASCIIがUTF-8のサブセットであるために発生するようです...

iconv US ASCII から UTF-8 または ISO-8859-15 へ

そして引用:

非ASCII文字が導入されるまで、テキストファイルを別の方法で表示する必要はありません

真実。ファイルに非 ASCII 文字を導入して保存すると、たとえばEclipseで、ファイルのエンコーディング (文字セット) が UTF-8 に切り替えられます。

私の場合、iconv に強制的にファイルを UTF-8 にトランスコードさせたいと思います。ASCII 以外の文字が含まれているかどうか。

注:理由は、私のPHPコード(非ASCIIファイル...)が非ASCII文字列を処理しているため、文字列が適切に解釈されないためです(フランス語):

Il était une fois... l'homme série animèe mythique d'Albert

Barille (Procidis), 1ère

...

  • US ASCII---- のサブセットですUTF-8(以下の Ned の回答を参照)
  • US ASCII ファイル実際にエンコードされていることを意味します。UTF-8
  • 私の問題は別の場所から来ました
4

12 に答える 12

86

ASCII は UTF-8 のサブセットであるため、すべての ASCII ファイルはすでに UTF-8 でエンコードされています。ASCII ファイル内のバイトと、「UTF-8 にエンコード」した結果のバイトは、まったく同じバイトになります。それらの間に違いはないので、何もする必要はありません。

問題は、ファイルが実際には ASCII ではないことです。使用しているエンコーディングを特定し、適切にトランスコードする必要があります。

于 2012-07-03T01:39:14.523 に答える
52

簡潔な答え

  • 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 番目の位置にあることはわかっています。その行を取得して、hexdump85 番目の位置から始まる 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 に指示したことだからです。d62 番目のウムラウトは、 (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...推測する前にファイル全体を強制的に調べたい場合は、うまくいくはずです。もちろん、これはfile5.26 以降を使用している場合にのみ機能します。

fileUS-ASCII の代わりに UTF-8 を強制的に表示する

file他の回答のいくつかは、ファイルにプレーンな 7 ビット ascii しか含まれていない場合でも、UTF-8 を表示しようとすることに焦点を当てているようです。これを熟考すれば、おそらくこれをやりたくないはずです。

  1. ファイルに 7 ビットの ascii しか含まれていないのにfile、ファイルが UTF-8 であるとコマンドが示している場合、そのファイルには UTF-8 固有のエンコーディングの文字が含まれていることを意味します。それが本当ではない場合、混乱や問題が発生する可能性があります。fileファイルに 7 ビットの ASCII 文字しか含まれていないときに UTF-8 で表示される場合、これはプログラムのバグですfile
  2. UTF-8 形式の入力ファイルを必要とするソフトウェアは、バイト レベルでは UTF-8 と同じであるため、プレーンな 7 ビット ascii の消費に問題はありません。ファイルを入力として受け入れる前にコマンド出力を使用しているソフトウェアがあり、fileUTF-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
于 2016-03-16T09:04:18.063 に答える
13

問題の核心は Nedにあると思います。あなたのファイルは実際には ASCII ではありません。試す

iconv -f ISO-8859-1 -t UTF-8 file.php > file-utf8.php

実際にISO 8859-1を使用していると推測しています。ほとんどのヨーロッパ言語で人気があります。

于 2012-07-03T01:58:48.160 に答える