Bagaimana saya bisa mendeteksi encoding / codepage dari file teks


295

Dalam aplikasi kami, kami menerima file teks ( .txt, .csv, dll) dari berbagai sumber. Saat membaca, file-file ini kadang-kadang mengandung sampah, karena file mana yang dibuat dalam codepage berbeda / tidak dikenal.

Apakah ada cara untuk (secara otomatis) mendeteksi codepage dari file teks?

The detectEncodingFromByteOrderMarks, pada StreamReaderkonstruktor, bekerja untuk UTF8 dan unicode ditandai file lainnya, tapi saya sedang mencari cara untuk mendeteksi halaman kode, seperti ibm850, windows1252.


Terima kasih atas jawaban Anda, inilah yang saya lakukan.

File yang kami terima adalah dari pengguna akhir, mereka tidak memiliki petunjuk tentang codepage. Penerima juga merupakan pengguna akhir, sekarang ini yang mereka ketahui tentang codepage: Codepage ada, dan menjengkelkan.

Larutan:

  • Buka file yang diterima di Notepad, lihat sepotong teks yang kacau. Jika seseorang disebut François atau sesuatu, dengan kecerdasan manusia Anda, Anda dapat menebaknya.
  • Saya telah membuat aplikasi kecil yang dapat digunakan pengguna untuk membuka file, dan memasukkan teks yang pengguna tahu akan muncul di file, ketika codepage yang benar digunakan.
  • Ulangi semua kode halaman, dan tampilkan yang memberikan solusi dengan teks yang disediakan pengguna.
  • Jika lebih dari satu codepage muncul, minta pengguna untuk menentukan lebih banyak teks.

Jawaban:


260

Anda tidak dapat mendeteksi codepage, Anda harus diberi tahu. Anda dapat menganalisis byte dan menebaknya, tetapi itu bisa memberikan hasil yang aneh (terkadang lucu). Saya tidak dapat menemukannya sekarang, tetapi saya yakin Notepad dapat diperdaya untuk menampilkan teks bahasa Inggris dalam bahasa Mandarin.

Bagaimanapun, ini adalah apa yang perlu Anda baca: Minimum Mutlak Setiap Pengembang Perangkat Lunak Sepenuhnya, Pasti Harus Tahu Tentang Unicode dan Karakter Set (Tanpa Alasan!) .

Secara khusus Joel mengatakan:

Satu-satunya Fakta Paling Penting Tentang Penyandian

Jika Anda benar-benar melupakan semua yang baru saja saya jelaskan, harap ingat satu fakta yang sangat penting. Tidak masuk akal memiliki string tanpa mengetahui pengkodean apa yang digunakannya. Anda tidak dapat lagi menjulurkan kepala ke pasir dan berpura-pura bahwa teks "biasa" adalah ASCII. Tidak Ada Hal Seperti Teks Biasa.

Jika Anda memiliki string, dalam memori, dalam file, atau dalam pesan email, Anda harus tahu apa itu pengkodean atau Anda tidak dapat menafsirkannya atau menampilkannya kepada pengguna dengan benar.


43
Saya menurunkan jawaban ini karena dua alasan. Pertama, mengatakan bahwa "Anda perlu diberi tahu" tidak membantu. Siapa yang akan memberi tahu saya, dan melalui media apa mereka akan melakukannya? Jika saya yang menyimpan file, siapa yang akan saya tanyakan? Diri? Kedua, artikel ini tidak terlalu bermanfaat sebagai sumber untuk menjawab pertanyaan. Artikel ini lebih merupakan sejarah pengodean yang ditulis dengan gaya David Sedaris. Saya menghargai narasinya, tetapi itu tidak langsung / langsung menjawab pertanyaan.
geneorama

9
@ Geneorama, saya pikir artikel Joel menjawab pertanyaan Anda lebih baik daripada yang pernah saya bisa, tapi begini ... Media pasti tergantung pada lingkungan di mana teks diterima. Lebih baik file (atau apa pun) berisi informasi itu (saya sedang memikirkan HTML dan XML). Kalau tidak, orang yang mengirim teks harus diizinkan untuk memberikan informasi itu. Jika Anda adalah orang yang membuat file, bagaimana Anda bisa tidak tahu apa pengkodean yang digunakannya?
JV.

4
@geneorama, lanjutan ... Akhirnya, saya kira alasan utama artikel itu tidak menjawab pertanyaan hanya karena tidak ada jawaban sederhana untuk pertanyaan itu. Jika pertanyaannya adalah "Bagaimana saya bisa menebak ..." maka saya akan menjawab secara berbeda.
JV.

1
@ JV Saya kemudian mengetahui bahwa xml / html dapat menentukan pengkodean karakter, terima kasih karena menyebutkan tidbit yang berguna.
geneorama

1
@JV "Buat file" mungkin pilihan kata yang buruk. Saya berasumsi bahwa pengguna dapat menentukan pengkodean file yang dihasilkan pengguna. Baru-baru ini saya "membuat" file dari Hadoop Cluster menggunakan Hive, dan meneruskannya ke FTP sebelum mengunduhnya ke berbagai mesin klien. Hasilnya memiliki beberapa unicode sampah di dalamnya, tapi saya tidak tahu langkah mana yang membuat masalah. Saya tidak pernah secara spesifik menentukan pengkodean. Saya berharap saya bisa memeriksa penyandian di setiap langkah.
geneorama

31

Jika Anda mencari untuk mendeteksi pengkodean non-UTF (yaitu tidak ada BOM), Anda pada dasarnya ke heuristik dan analisis statistik teks. Anda mungkin ingin melihat kertas Mozilla pada deteksi charset universal ( tautan yang sama, dengan pemformatan yang lebih baik melalui Wayback Machine ).


9
Lucunya instalasi Firefox 3.05 saya mendeteksi halaman itu sebagai UTF-8, menunjukkan sejumlah tanda tanya di mesin terbang intan, meskipun sumbernya memiliki tag meta untuk Windows-1252. Mengubah pengkodean karakter secara manual menunjukkan dokumen dengan benar.
devstuff

5
Kalimat Anda "Jika Anda mencari untuk mendeteksi pengkodean non-UTF (yaitu tidak ada BOM)" agak menyesatkan; standar unicode tidak merekomendasikan menambahkan BOM ke dokumen utf-8! (dan rekomendasi ini, atau ketiadaannya, adalah sumber dari banyak sakit kepala). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao

Ini dilakukan agar Anda dapat menggabungkan string UTF-8 tanpa mengakumulasi BOM yang berlebihan. Selain itu, Byte-Order Mark tidak diperlukan untuk UTF-8, tidak seperti UTF-16 misalnya.
sashoalm

26

Sudahkah Anda mencoba port C # untuk Mozilla Universal Charset Detector

Contoh dari http://code.google.com/p/ude/

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    

1
Bekerja dengan sempurna untuk tipe Windows-1252.
seebiscuit

Dan bagaimana Anda bisa menggunakannya untuk membaca file teks ke string menggunakan itu? CharsetDetector mengembalikan nama pengkodean dalam format string dan hanya itu ...
Bartosz

@ Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle

15

Anda tidak dapat mendeteksi codepage

Ini jelas salah. Setiap browser web memiliki semacam detektor charset universal untuk menangani halaman yang tidak memiliki indikasi penyandian apa pun. Firefox punya satu. Anda dapat mengunduh kode dan melihat bagaimana melakukannya. Lihat beberapa dokumentasi di sini . Pada dasarnya, ini heuristik, tetapi yang bekerja sangat baik.

Mengingat jumlah teks yang masuk akal, bahkan dimungkinkan untuk mendeteksi bahasa.

Ini satu lagi yang baru saja saya temukan menggunakan Google:


39
"heuristik" - jadi browser tidak cukup mendeteksinya, itu membuat tebakan yang berpendidikan. "Bekerja sangat baik" - jadi itu tidak berhasil sepanjang waktu? Kedengarannya saya seperti kita setuju.
JV.

10
Standar untuk HTML menentukan bahwa, jika set karakter tidak ditentukan oleh dokumen, maka harus dianggap dikodekan sebagai UTF-8.
Jon Trauntvein

5
Yang keren kecuali kita membaca dokumen HTML non-standar. Atau dokumen non-HTML.
Kos

2
Jawaban ini salah, jadi saya harus mengundurkan diri. Mengatakan itu salah bahwa Anda tidak dapat mendeteksi codepage, itu salah. Anda bisa menebak dan menebak Anda bisa jadi agak bagus, tetapi Anda tidak bisa "mendeteksi" sebuah codepage.
z80crew

1
@JonTrauntvein Menurut spesifikasi HTML5 a character encoding declaration is required even if the encoding is US-ASCII - deklarasi yang kurang menghasilkan penggunaan algoritma heuristik, tidak jatuh kembali ke UTF8.
z80crew

9

Saya tahu ini sudah sangat terlambat untuk pertanyaan ini dan solusi ini tidak akan menarik bagi beberapa orang (karena bias bahasa Inggris-sentris dan kurangnya statistik / pengujian empiris), tetapi ini bekerja sangat baik untuk saya, terutama untuk memproses data CSV yang diunggah:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Keuntungan:

  • Deteksi BOM bawaan
  • Pengkodean default / cadangan dapat disesuaikan
  • cukup dapat diandalkan (dalam pengalaman saya) untuk file berbasis Eropa-Eropa yang berisi beberapa data eksotis (misalnya nama Perancis) dengan campuran file gaya UTF-8 dan Latin-1 - pada dasarnya sebagian besar lingkungan AS dan Eropa barat.

Catatan: Akulah yang menulis kelas ini, jadi jelas bawa dengan sebutir garam! :)



7

Mencari solusi yang berbeda, saya menemukan itu

https://code.google.com/p/ude/

solusi ini agak berat.

Saya memerlukan beberapa deteksi dasar pengkodean, berdasarkan 4 byte pertama dan mungkin deteksi charset xml - jadi saya telah mengambil beberapa kode sumber sampel dari internet dan menambahkan versi modifikasi dari

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

ditulis untuk Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Cukup membaca mungkin 1024 byte pertama dari file, tapi saya memuat seluruh file.


7

Jika seseorang mencari solusi 93,9%. Ini bekerja untuk saya:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}

Solusi yang sangat bagus Seseorang dapat dengan mudah membungkus tubuh ReadAsString () dalam satu lingkaran penyandian yang diizinkan jika lebih dari 2 penyandian (UTF-8 dan ASCI 1252) harus diizinkan.
ViRuSTriNiTy

Setelah mencoba banyak contoh, saya akhirnya menemukan milik Anda. Saya di tempat yang bahagia sekarang. lol terima kasih !!!!!!!
Sedrick

Ini mungkin bukan jawaban untuk cara mendeteksi 1252 vs 1250, tetapi harus benar-benar menjadi jawaban untuk "Bagaimana mendeteksi UTF-8" dengan atau tanpa BOM !!
chuckc

4

Saya telah melakukan sesuatu yang serupa dengan Python. Pada dasarnya, Anda memerlukan banyak data sampel dari berbagai pengkodean, yang dipecah oleh jendela dua byte geser dan disimpan dalam kamus (hash), dikunci pada byte-pasangan yang memberikan nilai daftar pengkodean.

Dengan kamus (hash) itu, Anda mengambil teks input dan:

  • jika dimulai dengan karakter BOM ('\ xfe \ xff' untuk UTF-16-BE, '\ xff \ xfe' untuk UTF-16-LE, '\ xef \ xbb \ xbf' untuk UTF-8 dll), saya perlakukan seperti yang disarankan
  • jika tidak, maka ambil sampel teks yang cukup besar, ambil semua byte-pasang sampel dan pilih pengkodean yang paling jarang disarankan dari kamus.

Jika Anda juga mengambil sampel teks berkode UTF yang tidak dimulai dengan BOM apa pun, langkah kedua akan mencakup teks-teks yang tergelincir dari langkah pertama.

Sejauh ini, ini berfungsi untuk saya (data sampel dan data input berikutnya adalah subtitle dalam berbagai bahasa) dengan tingkat kesalahan yang semakin berkurang.


4

Alat "uchardet" melakukan ini dengan baik menggunakan model distribusi frekuensi karakter untuk setiap rangkaian karakter. File yang lebih besar dan lebih banyak "tipikal" file memiliki lebih banyak kepercayaan diri (jelas).

Di ubuntu, Anda baru saja apt-get install uchardet.

Di sistem lain, dapatkan sumber, penggunaan & dokumen di sini: https://github.com/BYVoid/uchardet


Pada Mac via homebrew:brew install uchardet
Paul B

3

Konstruktor kelas StreamReader mengambil parameter 'deteksi penyandian'.


Itu hanya tautan "penyandian" di sini .. dan uraiannya mengatakan bahwa kita harus menyediakan Penyandian ..
SurajS

@ SurajS: Lihat kelebihan lainnya.
leppie

penulis asli ingin mendeteksi penyandian file, yang berpotensi tidak memiliki BOM Marker. StreamReader mendeteksi pengkodean dari BOM Header sesuai tanda tangan. public StreamReader (Stream stream, bool detectEncodingFromByteOrderMarks)
ibondre

1

Jika Anda bisa menautkan ke pustaka C, Anda bisa menggunakan libenca. Lihat http://cihar.com/software/enca/ . Dari halaman manual:

Enca membaca file teks yang diberikan, atau input standar ketika tidak ada yang diberikan, dan menggunakan pengetahuan tentang bahasa mereka (harus didukung oleh Anda) dan campuran parsing, analisis statistik, menebak dan ilmu hitam untuk menentukan pengkodean mereka.

Ini GPL v2.


0

Mendapat masalah yang sama tetapi belum menemukan solusi yang baik untuk mendeteksinya secara otomatis. Sekarang saya menggunakan PsPad (www.pspad.com) untuk itu;) berfungsi dengan baik


0

Karena pada dasarnya turun ke heuristik, mungkin membantu menggunakan pengkodean file yang diterima sebelumnya dari sumber yang sama sebagai petunjuk pertama.

Kebanyakan orang (atau aplikasi) melakukan hal-hal dalam urutan yang hampir sama setiap kali, sering pada mesin yang sama, sehingga sangat mungkin bahwa ketika Bob membuat file .csv dan mengirimkannya ke Mary, ia akan selalu menggunakan Windows-1252 atau apa pun default mesinnya.

Jika memungkinkan, sedikit pelatihan pelanggan tidak akan menyakitkan :-)


0

Saya sebenarnya mencari cara pemrograman generik, bukan untuk mendeteksi penyandian file, tetapi saya belum menemukannya. Apa yang saya temukan dengan pengujian dengan pengkodean yang berbeda adalah bahwa teks saya adalah UTF-7.

Jadi di mana saya pertama kali lakukan: StreamReader file = File.OpenText (fullfilename);

Saya harus mengubahnya ke: StreamReader file = new StreamReader (fullfilename, System.Text.Encoding.UTF7);

OpenText menganggap itu UTF-8.

Anda juga dapat membuat StreamReader seperti StreamReader baru ini (fullfilename, true), parameter kedua yang berarti harus mencoba dan mendeteksi pengkodean dari byteordermark file, tetapi itu tidak berfungsi dalam kasus saya.


@JohnMachin Saya setuju bahwa ini jarang, tetapi diamanatkan misalnya di beberapa bagian protokol IMAP. Jika di situlah Anda berada, Anda tidak perlu menebak.
tripleee

0

Buka file di AkelPad (atau cukup salin / tempel teks yang kacau), buka Edit -> Selection -> Recode ... -> centang "Autodetect".


0

Sebagai tambahan pada posting ITmeze, saya telah menggunakan fungsi ini untuk mengubah output port C # untuk Mozilla Universal Charset Detector

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN



-1

Saya menggunakan kode ini untuk mendeteksi Unicode dan windows default ansi codepage ketika membaca file. Untuk pengkodean lain, pemeriksaan konten diperlukan, secara manual atau dengan pemrograman. Ini dapat digunakan untuk menyimpan teks dengan pengkodean yang sama seperti ketika dibuka. (Saya menggunakan VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()

-1

10Y (!) Telah berlalu sejak ini ditanyakan, dan saya masih melihat tidak menyebutkan solusi MS yang baik dan non-GPL: IMultiLanguage2 API.

Sebagian besar perpustakaan yang disebutkan berdasarkan pada UDE Mozilla - dan tampaknya masuk akal bahwa browser telah mengatasi masalah yang sama. Saya tidak tahu apa solusi chrome, tetapi karena IE 5.0 MS telah merilis solusi mereka, dan itu adalah:

  1. Bebas dari masalah lisensi GPL dan sejenisnya,
  2. Didukung dan dipelihara mungkin selamanya,
  3. Memberikan hasil yang kaya - semua kandidat yang sah untuk pengkodean / codepages bersama dengan skor kepercayaan,
  4. Sangat mudah digunakan (ini adalah panggilan fungsi tunggal).

Ini adalah panggilan COM asli, tapi di sini ada beberapa karya yang sangat bagus dari Carsten Zeumer, yang menangani kekacauan interop untuk penggunaan .net. Ada beberapa yang lain di sekitar, tetapi pada umumnya perpustakaan ini tidak mendapatkan perhatian yang layak.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.