web-dev-qa-db-ja.com

MacRoman、CP1252、Latin1、UTF-8、およびASCIIの間のエンコーディングを確実に推測する方法

職場では、エンコーディングに関連した接続、災難、または大惨事がなければ、一週間も経たないようです。この問題は通常、エンコーディングを指定せずに「テキスト」ファイルを確実に処理できると考えるプログラマーに由来します。しかし、できません。

そのため、今後*.txtまたは*.textで終わる名前を持つことをファイルに禁止することにしました。これらの拡張機能は、カジュアルなプログラマーをエンコードに関する鈍い自己満足に誤解させ、不適切な処理につながると考えられています。少なくとも何も持っていないことを知っているので、拡張子をまったく持たない方がほとんど良いでしょう。

しかし、私たちはそこまで行くつもりはありません。代わりに、エンコーディングで終わるファイル名を使用することが期待されます。たとえば、テキストファイルの場合、これらはREADME.asciiREADME.latin1README.utf8などのようになります。

特定の拡張子を必要とするファイルの場合、PerlやPythonなど、ファイル自体の内部でエンコードを指定できる場合は、それを行う必要があります。 Java sourceなど、ファイルの内部にそのような機能が存在しないソースの場合、SomeClass-utf8.Javaなどの拡張子の前にエンコードを配置します。

出力では、UTF-8が強く優先されます。

ただし、入力のために、*.txtという名前のコードベース内の数千のファイルを処理する方法を理解する必要があります。すべての名前を新しい標準に適合するように変更します。しかし、私たちはそれらをすべて見通すことはできません。したがって、実際に機能するライブラリまたはプログラムが必要です。

これらは、ASCII、ISO-8859-1、UTF-8、Microsoft CP1252、またはApple MacRomanです。ASCIIであるかどうかはわかりますが、おそらくUTF-8かどうかを知るための変更、8ビットエンコーディングについて困惑しています。ほとんどのデスクトップがMacである混合Unix環境(Solaris、Linux、Darwin)で実行しているため、迷惑なMacRomanファイル。これらは特に問題です。

しばらくの間、私はプログラムで決定する方法を探していました

  1. ASCII
  2. ISO-8859-1
  3. CP1252
  4. マクロマン
  5. UTF-8

ファイルが入っていて、これらの3つの異なる8ビットエンコーディングを確実に区別できるプログラムまたはライブラリが見つかりませんでした。たぶん、1000を超えるMacRomanファイルがあるだけなので、使用する文字セット検出器はどれでもそれらを検出する必要があります。私が見たものは何もトリックを管理できません。 ICU charset detector library に大きな期待がありましたが、MacRomanを処理できません。また、PerlとPythonの両方で同じ種類のことを行うモジュールについても見てきましたが、MacRomanの検出をサポートしていないということは何度も繰り返します。

したがって、私が探しているのは、ファイルがこれらの5つのエンコーディングのどれにあるか、そしてできればそれ以上であるかを確実に決定する既存のライブラリまたはプログラムです。特に、私が引用した3つの3ビットエンコーディング、特にMacRomanを区別する必要があります。ファイルは99%以上の英語のテキストです。他の言語にはいくつかありますが、多くはありません。

ライブラリコードの場合、言語設定は、Perl、C、Java、またはPythonで、その順序である必要があります。それが単なるプログラムである場合、完全なソースで提供され、Unix上で動作し、完全に邪魔されない限り、どの言語であるかはあまり気にしません。

ランダムにエンコードされた膨大な数のレガシーテキストファイルの問題が他にありましたか?もしそうなら、どのようにそれを解決しようとしましたか、そしてあなたはどれくらい成功しましたか?これが私の質問の最も重要な側面ですが、プログラマーがファイルの実際のエンコードを使用してファイルに名前を付ける(または名前を変更する)ことで、今後の問題の回避に役立つと思うかどうかにも興味があります。誰かがこれを制度的に強制しようとしたことがありますか?もしそうなら、that成功した​​かどうか、そしてなぜですか?

そして、はい、問題の性質を考えて明確な答えを保証できない理由を完全に理解しています。これは、小さなファイルの場合に特に当てはまります。小さなファイルでは、続行するのに十分なデータがありません。幸いなことに、私たちのファイルはめったに小さくありません。ランダムREADMEファイルを除いて、ほとんどは50kから250kのサイズ範囲にあり、多くはそれより大きいです。サイズが数Kを超えるものはすべて英語であることが保証されています。

問題の領域は生物医学のテキストマイニングであるため、PubMedCentralのすべてのOpen Accessリポジトリのように、大規模で非常に大きなコーパスを扱う場合があります。かなり大きなファイルは、5.7ギガバイトのBioThesaurus 6.0です。このファイルはほとんどすべてUTF-8であるため、特に迷惑です。しかし、いくつかのnumbskullは、いくつかの8ビットエンコーディングであるMicrosoft CP1252に含まれるいくつかの行を挿入しました。その上で旅行するまでにはかなり時間がかかります。 :(

99
tchrist

まず、簡単なケース:

ASCII

データに0x7Fを超えるバイトが含まれていない場合は、ASCIIです。 (または7ビットISO646エンコードですが、これらは非常に古いものです。)

UTF-8

データがUTF-8として検証される場合、それを安全に想定できますis UTF-8。 UTF-8の厳格な検証ルールにより、誤検知は非常にまれです。

ISO-8859-1対windows-1252

これら2つのエンコードの唯一の違いは、ISO-8859-1にはC1制御文字があり、windows-1252には印刷可能な文字があることです。 œžŸ。中かっこまたはダッシュを使用するファイルはたくさんありますが、C1制御文字を使用するファイルはありません。そのため、それらを気にせず、ISO-8859-1でさえ、代わりにwindows-1252を検出するだけです。

これで、質問が1つだけ残ります。

MacRomanとcp1252をどのように区別しますか?

これは非常に複雑です。

未定義の文字

バイト0x81、0x8D、0x8F、0x90、0x9Dは、windows-1252では使用されません。それらが発生する場合、データがMacRomanであると想定します。

同一の文字

バイト0xA2(¢)、0xA3(£)、0xA9(©)、0xB1(±)、0xB5(µ)は、両方のエンコーディングで同じです。これらが非ASCIIバイトのみである場合、MacRomanを選択するかcp1252を選択するかは問題ではありません。

統計的アプローチ

UTF-8であることがわかっているデータの文字(バイトではない!)の頻度をカウントします。最も頻繁な文字を決定します。次に、このデータを使用して、cp1252またはMacRoman文字がより一般的かどうかを判断します。

たとえば、英語のウィキペディアの100のランダムな記事で実行した検索で、最も一般的な非ASCII文字は·•–é°®’èö—。この事実に基づいて、

  • バイト0x92、0x95、0x96、0x97、0xAE、0xB0、0xB7、0xE8、0xE9、または0xF6は、windows-1252を示唆しています。
  • バイト0x8E、0x8F、0x9A、0xA1、0xA5、0xA8、0xD0、0xD1、0xD5、または0xE1はMacRomanを示唆しています。

Cp1252を提案するバイトとMacRomanを提案するバイトをカウントアップし、どちらか大きい方を選びます。

85
dan04

Mozilla nsUniversalDetector (Perlバインディング: Encode :: Detect / Encode :: Detect :: Detector )は100万倍の実績があります。

10
daxim

このようなヒューリスティックでの私の試み(ASCIIおよびUTF-8)を除外したと仮定すると):

  • 0x7f〜0x9fがまったく表示されない場合は、ISO-8859-1である可能性があります。これらは非常にまれにしか使用されていないためです。
  • 0x91〜0x94が多く表示される場合は、Windows-1252である可能性があります。これは、これらが「スマートクォート」であり、その範囲で最も可能性の高い文字が英語テキストで使用されるためです。より確実にするには、ペアを探すことができます。
  • それ以外の場合、特に0xd2から0xd5が多く表示される場合は、MacRomanです(MacRomanでのタイポグラフィ引用符の位置です)。

サイドノート:

Java sourceなど、ファイルの内部にそのような機能が存在しないソースの場合、SomeClass-utf8.Javaなどの拡張子の前にエンコーディングを配置します。

これをしないでください!!

Javaコンパイラはファイル名がクラス名と一致することを期待しているため、ファイルの名前を変更するとソースコードがコンパイルできなくなります。正しいことはエンコーディングを推測し、 native2ascii すべての非ASCII文字を nicodeエスケープシーケンス に変換するツール。

7

「Perl、C、Java、またはPython、そしてその順序で」:興味深い態度:-)

「何かがおそらくUTF-8であるかどうかを知ることの良い変化に耐えます」:実際には、高ビットセットバイトを使用する他の文字セットでエンコードされた意味のあるテキストを含むファイルは、UTF-8が非常に小さいため、正常にデコードされます。

UTF-8戦略(最も優先度の低い言語):

# 100% Unicode-standard-compliant UTF-8
def utf8_strict(text):
    try:
        text.decode('utf8')
        return True
    except UnicodeDecodeError:
        return False

# looking for almost all UTF-8 with some junk
def utf8_replace(text):
    utext = text.decode('utf8', 'replace')
    dodgy_count = utext.count(u'\uFFFD') 
    return dodgy_count, utext
    # further action depends on how large dodgy_count / float(len(utext)) is

# checking for UTF-8 structure but non-compliant
# e.g. encoded surrogates, not minimal length, more than 4 bytes:
# Can be done with a regex, if you need it

ASCIIでもUTF-8でもないことを決定したら:

私が知っているMozilla-Origin文字セット検出器はMacRomanをサポートせず、いずれの場合も特に英語では8ビット文字セットで良い仕事をしません。言語、句読点文字を無視し、その言語の幅広いドキュメントに基づいています。

他の人が言ったように、cp1252とマクロマンを区別するために使用できるのは、実際に高ビットセットの句読文字のみです。シェークスピアやハンサード、KJV聖書ではなく、あなた自身のドキュメントでMozillaタイプのモデルをトレーニングし、256バイトすべてを考慮に入れることをお勧めします。あなたのファイルにはマークアップ(HTML、XMLなど)が含まれていないことを前提としています。

ほとんどUTF-8であるがデコードに失敗したファイルについて言及しました。また、以下についても非常に疑わなければなりません。

(1)ISO-8859-1でエンコードされているが、0x80〜0x9Fの範囲の「制御文字」を含むファイル...これは非常に普及しているため、ドラフトHTML5標準ではデコードするように指示されています[〜#〜] all [〜#〜]cp1252を使用してISO-8859-1として宣言されたHTMLストリーム。

(2)OKをUTF-8としてデコードするが、結果のUnicodeにはU + 0080からU + 009Fまでの範囲の「制御文字」が含まれるファイル...これは、cp1252/cp850のトランスコーディングの結果として発生する可能性があります「ISO-8859-1」からUTF-8までのファイル。

背景:私は、Webベースではなくファイル指向で、cp850やcp437のようなlegacy ** nを含む8ビット文字セットで適切に動作するPythonベースの文字セット検出器を作成するための、ウェットサンデーアフタヌーンプロジェクトを持っています。まだプライムタイムに近いところはありません。トレーニングファイルに興味があります。あなたのISO-8859-1/cp1252/MacRomanファイルは、誰のコードソリューションでも期待されるのと同じくらい「ゆったり」していますか?

6
John Machin

発見したように、この問題を解決する完璧な方法はありません。ファイルがどのエンコーディングを使用するかに関する暗黙の知識がなければ、すべての8ビットエンコーディングはまったく同じであるためです:バイトのコレクション。すべてのバイトは、すべての8ビットエンコーディングに有効です。

期待できる最善の方法は、バイトを分析するある種のアルゴリズムであり、特定のエンコーディングで特定の言語で使用されている特定のバイトの確率に基づいて、ファイルが使用するエンコーディングを推測します。ただし、ファイルが使用する言語を知る必要があり、エンコードが混在するファイルがある場合は完全に役に立たなくなります。

利点として、ファイル内のテキストが英語で書かれていることがわかっている場合、言及されたすべてのエンコーディングの違いはすべてローカライズされているため、そのファイルに使用することに決めたエンコーディングに違いはありません英語では通常使用されない文字を指定するエンコーディングの部分。テキストに特別なフォーマットや句読点の特別なバージョン(CP1252にはいくつかのバージョンの引用文字が含まれるなど)を使用する場合、いくつかの問題が発生する可能性がありますが、テキストの要旨についてはおそらく問題はありません。

3
Epcylon

ランダムにエンコードされた膨大な数のレガシーテキストファイルの問題が他にありましたか?もしそうなら、どのようにそれを解決しようとしましたか、そしてあなたはどれくらい成功しましたか?

現在、ファイルをXMLに変換するプログラムを書いています。各ファイルのタイプを自動検出する必要があります。これは、テキストファイルのエンコーディングを決定する問題のスーパーセットです。エンコーディングを決定するために、私はベイジアンアプローチを使用しています。つまり、私の分類コードは、テキストファイルが理解するすべてのエンコーディングに対して特定のエンコーディングを持つ確率(尤度)を計算します。次に、プログラムは最も可能性の高いデコーダーを選択します。ベイジアンのアプローチは、各エンコーディングに対してこのように機能します。

  1. 各エンコードの頻度に基づいて、ファイルがエンコードされている初期(prior)確率を設定します。
  2. ファイル内の各バイトを順番に調べます。バイト値をルックアップして、存在するバイト値と実際にそのエンコーディングにあるファイルとの相関を判断します。その相関を使用して、ファイルがエンコードされている新しい(posterior)確率を計算します。調べるバイトがさらにある場合は、次のバイトを調べるときに、そのバイトの事後確率を事前確率として使用します。
  3. ファイルの最後に到達すると(実際には最初の1024バイトのみを見る)、あなたが持っている可能性は、ファイルがエンコードされている確率です。

ベイズの定理がveryになることを証明します。確率を計算する代わりに、information contentを計算すると、oddsinfo = log(p / (1.0 - p))の対数です。

手動で分類したファイルのコーパスを調べることにより、内部の事前確率と相関を計算する必要があります。

1
Raedwald

マクロマンを除くすべてのエンコーディングを検出できる場合、解読できないものはマクロマンにあると仮定するのは論理的です。つまり、処理できなかったファイルのリストを作成し、それらをマクロマンであるかのように処理します。

これらのファイルをソートする別の方法は、文字化けしていないエンコーディングをユーザーが決定できるサーバーベースのプログラムを作成することです。もちろん、それは社内にありますが、100人の従業員が毎日数人の作業を行っているため、数千のファイルがすぐに処理されます。

最後に、既存のすべてのファイルを単一の形式に変換し、新しいファイルがその形式であることを要求する方が良いでしょうか。

1
Eric Pauley