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


99

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

したがって、今後、ファイルの末尾が*.txtまたはで終わる名前をファイルに付けることを禁止することが決定されました*.text。その考えは、これらの拡張機能により、カジュアルなプログラマーをエンコードに関する鈍い自己満足に導き、これが不適切な処理につながるということです。少なくともあなたが知っているので、それは全く拡張を持たない方がほぼ良いでしょうあなたは、あなたが持っているかわからないということ。

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

特定の拡張子が必要なファイルの場合、PerlやPythonなど、ファイル自体の内部でエンコーディングを指定できる場合は、それを行う必要があります。そのような機能がファイルの内部に存在しないJavaソースのようなファイルの場合、次のように、エンコーディングを拡張の前に置きます。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ビットエンコーディングを確実に区別できるプログラムまたはライブラリが見つかりません。おそらく、1,000以上のMacRomanファイルだけが存在するため、私たちが使用する文字セット検出器は、それらを探知できなければなりません。私が見たものは何もトリックを管理できません。ICU文字セット検出器ライブラリに大きな期待がありましたに MacRomanを処理できません。また、PerlとPythonの両方で同じ種類のことを行うためのモジュールも調べましたが、何度も繰り返しますが、MacRomanの検出はサポートされていません。

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

ライブラリコードの場合、言語設定は、Perl、C、Java、またはPythonの順で、それが単なるプログラムである場合、完全なソースで提供され、Unixで実行され、完全に邪魔にならない限り、その言語がどの言語であるかは特に問題になりません。

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

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

問題ドメインは生物医学のテキストマイニングであるため、PubMedCentralのすべてのオープンアクセスリポジトリのように、大規模で非常に大規模なコーパスを扱う場合があります。かなり巨大なファイルは5.7ギガバイトのBioThesaurus 6.0です。このファイルはほとんどすべてUTF-8であるため、特に煩わしいものです。しかし、一部の麻痺した頭蓋骨は、8ビットエンコーディングであるMicrosoft CP1252のいくつかの行に行き詰まりました。あなたがそれをトリップするまでにはかなり時間がかかります。:(


解決策については、stackoverflow.com
questions / 4255305 /…を

回答:


86

まず、簡単なケース:

ASCII

データに0x7Fより上のバイトが含まれていない場合、それはASCIIです。(または7ビットISO646エンコーディングですが、これらは非常に古いものです。)

UTF-8

UTF-8のように、あなたのデータを検証した場合は、安全に、それは想定できている UTF-8。UTF-8の厳密な検証ルールにより、誤検知は非常にまれです。

ISO-8859-1対windows-1252

これら2つのエンコーディングの唯一の違いは、ISO-8859-1にC1制御文字があり、windows-1252に印刷可能な文字があることです。 œžŸ。中かっこやダッシュを使用するファイルはたくさんありますが、C1制御文字を使用するファイルはありません。そのため、それらを気にしないでください。代わりに、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のランダムな英語のWikipedia記事に対して実行したばかりの検索で、最も一般的な非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-suggestingバイトとMacRoman-suggestingバイトを数え、どちらか大きい方を使います。


6
私はあなたの答えを受け入れました。なぜなら、より良いものはそれ自体を提示していないからです。あなたは私がいじくり回してきた問題そのものを書くのに良い仕事をしました。実際、私はそれらのバイトを盗聴するプログラムを持っていますが、私が思いついた数の約2倍です。
tchrist 2010年

10
最後に、これを実装することにしました。ウィキペディアは良いトレーニングデータではないことがわかりました。言語セクションを含まない1kのランダムなen.wikipedia記事から、私は50AのunASCIIコードポイントを取得しましたが、分布は信用できません:真ん中のドットと箇条書きが高すぎます&c&c&c。そこで、UTF8のすべてのPubMedオープンアクセスコーパスを使用して、+ 14MのunASCIIコードポイントをマイニングしました。これらを使用して、すべての8ビットエンコーディングの相対周波数モデルを構築します。これは、あなたのモデルよりも洗練されていますが、そのアイデアに基づいています。これは、標的領域である生物医学テキストのエンコーディングを非常に予測できることを証明しています。これを公開します。ありがとう!
tchrist

5
MacRomanファイルはまだありませんが、行区切り文字が有用なテストを提供するため、CRを使用しません。これは古いバージョンのMac OSで機能しますが、OS9については知りません。
Milliways 2012

10

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


その他のドキュメントはここにあります:mozilla.org/projects/intl/detectorsrc.html、そこから、ドキュメントを詳しく調べれば、サポートされている文字セットを見つけることができます
Joel Berger

@ジョエル:私はソースを掘り下げました。それは修辞的な質問でした。x-mac-cyrillicサポートx-mac-hebrewされており、コメントで詳細に説明されてx-mac-anything-elseいますが、言及されていません。
John Machin、

@John Machin:キリル文字とヘブライ語がうなずくのは奇妙だが、他には何もない 私は別のドキュメンテーションソースを投げるだけでした、それ以上読んでいませんでした、ありがとう!
Joel Berger、

7

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

  • 0x7fから0x9fがまったく表示されない場合は、ISO-8859-1である可能性があります。これらはほとんど使用されない制御コードであるためです。
  • 0x91から0x94までが大量に表示される場合は、おそらくWindows-1252です。これらは「スマートクォート」であるため、英語のテキストで使用されるその範囲で最も可能性の高い文字です。より確実にするために、ペアを探すことができます。
  • それ以外の場合は、特に0xd2から0xd5までの多くが表示されている場合はMacRomanです(MacRomanで表記上の引用が行われている場所です)。

サイドノート:

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

こんなことしないで!!

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


7
愚かなコンピロール!いいえ、ASCIIしか使用できないとは言えません。これはもう1960年代ではありません。@encodingアノテーションがあり、ソースが特定のエンコーディングであるという事実がソースコードの外部に格納されることを強制されなかった場合は問題になりません。PerlもPythonも影響を受けないJavaの本当にばかげた欠点です。 。ソースにある必要があります。それは私たちの主な問題ではありません。それは数千の*.textファイルです。
tchrist 2010年

3
@tchrist:実際に、そのような注釈をサポートするために独自の注釈プロセッサを書くことはそれほど難しくありません。それでも、標準APIにはないという恥ずかしい見落としがあります。
Michael Borgwardt、

Javaが@encodingをサポートしていたとしても、それはエンコーディング宣言が正しいことを保証するものではありません。
dan04 2010年

4
@ dan04:XML、HTML、または他の場所でのエンコーディング宣言についても同じことが言えます。しかし、これらの例と同様に、標準APIで定義されている場合、ソースコードで動作するほとんどのツール(特にエディタとIDE)はそれをサポートし、コンテンツのエンコーディングが一致しないファイルを誤って作成することを確実に防ぎます。宣言。
Michael Borgwardt、2010年

4
「Javaコンパイラは、ファイル名がクラス名と一致することを期待しています。」このルールは、ファイルがトップレベルのパブリッククラスを定義している場合にのみ適用されます。
Matthew Flaschen、2010年

6

「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起源の文字セット検出器はMacRomanをサポートしておらず、特に英語では、8ビット文字セットではうまく機能しません。句読文字を無視し、その言語のドキュメントの幅広い選択に基づいた言語。

他の人が述べたように、実際に使用できるのは、cp1252とmacromanを区別するために使用できる高ビットセットの句読文字だけです。シェイクスピアやハンサード、KJV聖書ではなく、自分のドキュメントでMozillaタイプのモデルをトレーニングし、256バイトすべてを考慮に入れることをお勧めします。あなたのファイルにはマークアップ(HTML、XMLなど)が含まれていないと思います-何か衝撃的な確率を歪めることになります。

あなたはほとんどUTF-8であるが、デコードに失敗するファイルについて言及しました。また、次の点にも非常に注意する必要があります。

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

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

背景:湿った日曜日の午後のプロジェクトで、(Web指向ではなく)ファイル指向でlegacy ** n、cp850やcp437 などの8ビット文字セットで適切に機能するPythonベースの文字セット検出器を作成しています。まだプライムタイムにはほど遠い。トレーニングファイルに興味があります。あなたのISO-8859-1 / cp1252 / MacRomanファイルは、誰かのコードソリューションがそうであると期待するのと同じように「アンカンバー」されていますか?


1
言語の順序付けの理由は環境です。私たちの主要なアプリケーションのほとんどは、Javaとマイナーユーティリティにある傾向があり、一部のアプリケーションはPerlにあります。Pythonにあるコードは、あちこちにあります。私はほとんどがCとperlのプログラマーですが、少なくとも最初の選択では、アプリライブラリにプラグインするJavaソリューションか、同じもののperlライブラリを探していました。Cの場合、XS接着層を作成してそれをperlインターフェースに接続できますが、Pythonでこれを行ったことはありません。
tchrist 2010年

3

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

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

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


1

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

これらのファイルを並べ替える別の方法は、ユーザーが文字化けしないかどうかをユーザーが決定できるサーバーベースのプログラムを作成することです。もちろん、それは社内にありますが、100人の従業員が毎日数人を数えるので、何千ものファイルがすぐに完成します。

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


5
おかしい!30分間中断された後、このコメントを最初に読んだとき、「マクロマン」を「マクロマン」と読み、OPがそれを言及しているかどうかを確認するためにその文字列の検索を実行するまで、MacRomanとの接続を確立しませんでした。
Adrian Pronk、2010年

+1この答えはちょっと面白いです。それが良いアイデアか悪いアイデアかはわかりません。誰もが検出されない可能性がある既存のエンコーディングを考えることができますか?将来的に存在する可能性がありますか?
ユーザー名:

1

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

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

  1. イニシャル(各エンコーディングの頻度に基づいて、ファイルがエンコーディング内に)確率を設定します。
  2. ファイル内の各バイトを順番に調べます。バイト値を調べて、そのバイト値が存在することと、実際にそのエンコーディングにあるファイルとの間の相関関係を判別します。その相関関係を使用して、新しい(事後て、ファイルがエンコードされて)確率します。調べるバイトが多い場合は、次のバイトを調べるときに、そのバイトの事後確率を事前確率として使用します。
  3. ファイルの最後に到達すると(実際には最初の1024バイトのみを確認しています)、ファイルがエンコードされている可能性が高くなります。

これは、ベイズの定理になることを蒸散非常に代わり確率を計算するので、あなたが計算場合の対処しやすい情報内容の対数で、オッズをinfo = log(p / (1.0 - p))

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.