Làm cách nào để phát hiện mã hóa / bảng mã của tệp văn bản


295

Trong ứng dụng của chúng tôi, chúng tôi nhận được tập tin văn bản ( .txt, .csv, vv) từ các nguồn khác nhau. Khi đọc, các tệp này đôi khi chứa rác, vì các tệp được tạo trong một bảng mã khác / không xác định.

Có cách nào để (tự động) phát hiện bảng mã của tệp văn bản không?

Các detectEncodingFromByteOrderMarks, trên StreamReaderconstructor, làm việc cho UTF8 và các file unicode đánh dấu khác, nhưng tôi đang tìm kiếm một cách để phát hiện các trang mã, như ibm850, windows1252.


Cảm ơn câu trả lời của bạn, đây là những gì tôi đã làm.

Các tệp chúng tôi nhận được là từ người dùng cuối, họ không có manh mối về tiền mã hóa. Người nhận cũng là người dùng cuối, đến bây giờ đây là những gì họ biết về tiền mã hóa: Tiền mã hóa tồn tại và gây phiền nhiễu.

Giải pháp:

  • Mở tệp đã nhận trong Notepad, nhìn vào một đoạn văn bản bị cắt xén. Nếu ai đó được gọi là François hoặc một cái gì đó, với trí thông minh con người của bạn, bạn có thể đoán điều này.
  • Tôi đã tạo một ứng dụng nhỏ mà người dùng có thể sử dụng để mở tệp và nhập văn bản mà người dùng biết nó sẽ xuất hiện trong tệp khi sử dụng mã chính xác.
  • Lặp lại tất cả các bảng mã và hiển thị những giải pháp đưa ra giải pháp với văn bản do người dùng cung cấp.
  • Nếu có nhiều hơn một bảng mã bật lên, hãy yêu cầu người dùng chỉ định thêm văn bản.

Câu trả lời:


260

Bạn không thể phát hiện codepage, bạn cần phải nói với nó. Bạn có thể phân tích các byte và đoán nó, nhưng điều đó có thể cho một số kết quả kỳ lạ (đôi khi gây cười). Bây giờ tôi không thể tìm thấy nó, nhưng tôi chắc rằng Notepad có thể bị lừa hiển thị văn bản tiếng Anh bằng tiếng Trung.

Dù sao, đây là những gì bạn cần đọc: Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Tích cực phải biết về Unicode và Bộ ký tự (Không có lý do!) .

Cụ thể Joel nói:

Sự thật quan trọng nhất về mã hóa

Nếu bạn hoàn toàn quên mọi thứ tôi vừa giải thích, xin hãy nhớ một sự thật cực kỳ quan trọng. Không có nghĩa là có một chuỗi mà không biết nó sử dụng mã hóa gì. Bạn không còn có thể dính đầu vào cát và giả vờ rằng văn bản "đơn giản" là ASCII. Không có điều gì như văn bản đơn giản.

Nếu bạn có một chuỗi, trong bộ nhớ, trong tệp hoặc trong email, bạn phải biết mã hóa đó là gì hoặc bạn không thể giải thích nó hoặc hiển thị chính xác cho người dùng.


43
Tôi đánh giá thấp câu trả lời này vì hai lý do. Đầu tiên, nói rằng "bạn cần được nói" không hữu ích. Ai sẽ nói với tôi, và thông qua phương tiện nào họ sẽ làm như vậy? Nếu tôi là người đã lưu tệp, tôi sẽ hỏi ai? Riêng tôi? Thứ hai, bài viết không đặc biệt hữu ích như một tài nguyên để trả lời câu hỏi. Bài viết này là một lịch sử của mã hóa được viết theo phong cách David Sedaris. Tôi đánh giá cao câu chuyện, nhưng nó không chỉ đơn giản / trực tiếp trả lời câu hỏi.
genorama

9
@geneorama, tôi nghĩ rằng bài viết của Joel giải quyết các câu hỏi của bạn tốt hơn bao giờ hết, nhưng ở đây đi ... Phương tiện chắc chắn phụ thuộc vào môi trường mà văn bản được nhận. Tốt hơn là tệp (hoặc bất cứ thứ gì) chứa thông tin đó (Tôi đang nghĩ HTML và XML). Nếu không, người gửi văn bản nên được phép cung cấp thông tin đó. Nếu bạn là người tạo ra tệp, làm sao bạn không biết nó sử dụng mã hóa nào?
Liên doanh.

4
@geneorama, tiếp tục ... Cuối cùng, tôi cho rằng lý do chính khiến bài báo không trả lời câu hỏi đơn giản là vì không có câu trả lời đơn giản cho câu hỏi đó. Nếu câu hỏi là "Làm sao tôi đoán được ..." thì tôi đã trả lời khác đi.
Liên doanh.

1
@JV Sau này tôi mới biết rằng xml / html có thể chỉ định mã hóa ký tự, cảm ơn vì đã đề cập đến mẩu tin hữu ích đó.
genorama

1
@JV "Tạo một tệp" có thể là một lựa chọn từ kém. Tôi giả sử rằng người dùng có thể chỉ định mã hóa tệp mà người dùng tạo. Gần đây tôi đã "tạo" một tệp từ Cụm Hadoop bằng Hive và chuyển nó sang FTP trước khi tải xuống các máy khách khác nhau. Kết quả có một số rác unicode trong đó, nhưng tôi không biết bước nào tạo ra vấn đề. Tôi đã không bao giờ xác định rõ ràng mã hóa. Tôi ước rằng tôi có thể kiểm tra mã hóa ở mỗi bước.
genorama

31

Nếu bạn đang tìm cách phát hiện các bảng mã không phải UTF (tức là không có BOM), thì về cơ bản bạn sẽ phân tích theo kinh nghiệm và phân tích thống kê của văn bản. Bạn có thể muốn xem bài báo Mozilla về phát hiện bộ ký tự phổ quát ( cùng liên kết, với định dạng tốt hơn thông qua Wayback Machine ).


9
Thật thú vị, bản cài đặt Firefox 3.05 của tôi phát hiện trang đó là UTF-8, hiển thị một số glyphs-in-a-diamond kim cương, mặc dù nguồn có thẻ meta cho Windows-1252. Thay đổi thủ công mã hóa ký tự hiển thị tài liệu chính xác.
đồ sộ

5
Câu của bạn "Nếu bạn đang tìm cách phát hiện các mã hóa không phải UTF (tức là không có BOM)" thì hơi sai; tiêu chuẩn unicode không khuyến nghị thêm BOM vào tài liệu utf-8! (và khuyến nghị này, hoặc thiếu nó, là nguồn gốc của nhiều vấn đề đau đầu). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Tao

Điều này được thực hiện để bạn có thể nối các chuỗi UTF-8 mà không tích lũy BOM dự phòng. Ngoài ra, không cần có Dấu Byte-Order cho UTF-8, không giống như UTF-16 chẳng hạn.
sashoalm

26

Bạn đã thử cổng C # cho Mozilla Universal Charset dò

Ví dụ từ http://code.google.com.vn/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
Hoạt động hoàn hảo cho loại Windows-1252.
seebcakes

Và làm thế nào bạn có thể sử dụng nó để đọc một tệp văn bản để xâu chuỗi bằng cách đó? CharsetDetector trả về tên của mã hóa ở định dạng chuỗi và đó là ...
Bartosz

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

15

Bạn không thể phát hiện bảng mã

Điều này rõ ràng là sai. Mỗi trình duyệt web có một loại trình phát hiện ký tự phổ quát để xử lý các trang không có dấu hiệu nào của mã hóa. Firefox có một cái. Bạn có thể tải mã và xem nó làm như thế nào. Xem một số tài liệu ở đây . Về cơ bản, nó là một heuristic, nhưng một trong đó hoạt động thực sự tốt.

Cho một lượng văn bản hợp lý, thậm chí có thể phát hiện ngôn ngữ.

Đây là một cái khác tôi vừa tìm thấy bằng Google:


39
"heuristic" - vì vậy trình duyệt không hoàn toàn phát hiện ra nó, nó tạo ra một phỏng đoán có giáo dục. "hoạt động thực sự tốt" - vì vậy nó không hoạt động mọi lúc? Âm thanh với tôi như chúng ta đồng ý.
Liên doanh.

10
Tiêu chuẩn cho HTML ra lệnh rằng, nếu bộ ký tự không được xác định bởi tài liệu, thì nó nên được coi là được mã hóa dưới dạng UTF-8.
Jon Trauntvein

5
Thật tuyệt vời trừ khi chúng ta đọc các tài liệu HTML không chuẩn. Hoặc các tài liệu không phải HTML.
Kos

2
Câu trả lời này là sai, vì vậy tôi đã phải downvote. Nói rằng nó sai khi bạn không thể phát hiện ra bảng mã, là sai. Bạn có thể đoán và dự đoán của bạn có thể khá tốt, nhưng bạn không thể "phát hiện" một bảng mã.
z80crew

1
@JonTrauntvein Theo thông số kỹ thuật HTML5 a character encoding declaration is required even if the encoding is US-ASCII - một tuyên bố thiếu kết quả trong việc sử dụng thuật toán heuristic, không rơi vào UTF8.
z80crew

9

Tôi biết câu hỏi này rất muộn và giải pháp này sẽ không hấp dẫn đối với một số người (vì thiên vị trung tâm tiếng Anh và thiếu kiểm tra thống kê / thực nghiệm), nhưng nó hoạt động rất tốt đối với tôi, đặc biệt là xử lý dữ liệu CSV đã tải lên:

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

Ưu điểm:

  • Phát hiện BOM tích hợp
  • Mã hóa mặc định / dự phòng tùy chỉnh
  • khá đáng tin cậy (theo kinh nghiệm của tôi) đối với các tệp có trụ sở ở Tây Âu có chứa một số dữ liệu kỳ lạ (ví dụ như tên tiếng Pháp) với hỗn hợp các tệp kiểu UTF-8 và Latin-1 - về cơ bản là phần lớn môi trường Hoa Kỳ và Tây Âu.

Lưu ý: Tôi là người đã viết lớp này, vì vậy rõ ràng hãy dùng nó với một hạt muối! :)



7

Tìm kiếm giải pháp khác nhau, tôi thấy rằng

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

Giải pháp này hơi nặng.

Tôi cần một số phát hiện mã hóa cơ bản, dựa trên 4 byte đầu tiên và có thể là phát hiện bộ ký tự xml - vì vậy tôi đã lấy một số mã nguồn mẫu từ internet và thêm phiên bản sửa đổi một chút của

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

được viết cho 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
    }

Có thể đọc 1024 byte đầu tiên từ tệp, nhưng tôi đang tải toàn bộ tệp.


7

Nếu ai đó đang tìm kiếm một giải pháp 93,9%. Điều này làm việc cho tôi:

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();
        }
    }
}

Giải pháp rất hay. Người ta có thể dễ dàng bọc phần thân của ReadAsString () trong một vòng mã hóa được phép nếu cho phép nhiều hơn 2 mã hóa (UTF-8 và ASCI 1252).
ViRuSTriNiTy

Sau khi thử hàng tấn ví dụ, cuối cùng tôi cũng đã đến với bạn. Tôi đang ở một nơi hạnh phúc ngay bây giờ. lol cảm ơn !!!!!!!
Sedrick

Đây có thể không phải là câu trả lời cho cách phát hiện 1252 so với 1250, nhưng nó hoàn toàn nên là câu trả lời cho "Cách phát hiện UTF-8" có hoặc không có BOM !!
chuckc

4

Tôi đã làm một cái gì đó tương tự trong Python. Về cơ bản, bạn cần rất nhiều dữ liệu mẫu từ các bảng mã khác nhau, được chia nhỏ bằng cửa sổ hai byte trượt và được lưu trữ trong từ điển (hàm băm), được khóa trên các cặp byte cung cấp các giá trị của danh sách mã hóa.

Cho từ điển đó (hàm băm), bạn lấy văn bản đầu vào của mình và:

  • nếu nó bắt đầu bằng bất kỳ ký tự BOM nào ('\ xfe \ xff' cho UTF-16-BE, '\ xff \ xfe' cho UTF-16-LE, '\ xef \ xbb \ xbf' cho UTF-8, v.v.), tôi coi nó như đề nghị
  • nếu không, sau đó lấy một mẫu văn bản đủ lớn, lấy tất cả các cặp byte của mẫu và chọn mã hóa được đề xuất ít phổ biến nhất từ ​​từ điển.

Nếu bạn cũng đã lấy mẫu các văn bản được mã hóa UTF không bắt đầu bằng bất kỳ BOM nào, bước thứ hai sẽ bao gồm các văn bản bị trượt từ bước đầu tiên.

Cho đến nay, nó hoạt động với tôi (dữ liệu mẫu và dữ liệu đầu vào tiếp theo là phụ đề bằng nhiều ngôn ngữ khác nhau) với tỷ lệ lỗi giảm dần.


4

Công cụ "uchardet" thực hiện tốt điều này bằng cách sử dụng các mô hình phân phối tần số ký tự cho mỗi bộ ký tự. Các tệp lớn hơn và các tệp "điển hình" hơn có độ tin cậy cao hơn (rõ ràng).

Trên Ubuntu, bạn chỉ cần apt-get install uchardet.

Trên các hệ thống khác, hãy lấy nguồn, cách sử dụng & tài liệu tại đây: https://github.com/BYVoid/uchardet


Trên Mac thông qua homebrew:brew install uchardet
Paul B

3

Hàm tạo của lớp StreamReader lấy tham số 'phát hiện mã hóa'.


Đây chỉ là liên kết "mã hóa" ở đây .. và mô tả nói rằng chúng tôi phải cung cấp Mã hóa ..
SurajS

@SurajS: Nhìn vào sự quá tải khác.
leppie

tác giả ban đầu muốn phát hiện mã hóa cho một tệp, có khả năng không có BOM Marker. StreamReader phát hiện mã hóa từ BOM Header theo chữ ký. công khai StreamReader (Luồng phát trực tuyến, phát hiện
boolEncodingFromByteOrderMarks

1

Nếu bạn có thể liên kết đến thư viện C, bạn có thể sử dụng libenca. Xem http://cihar.com/software/enca/ . Từ trang người đàn ông:

Enca đọc các tệp văn bản đã cho hoặc đầu vào tiêu chuẩn khi không được cung cấp và sử dụng kiến ​​thức về ngôn ngữ của họ (phải được bạn hỗ trợ) và hỗn hợp phân tích, phân tích thống kê, đoán và ma thuật đen để xác định mã hóa của chúng.

Đó là GPL v2.


0

Có cùng một vấn đề nhưng chưa tìm thấy một giải pháp tốt để phát hiện nó tự động. Bây giờ tôi đang sử dụng PsPad (www.pspad.com) cho điều đó;) Hoạt động tốt


0

Vì về cơ bản nó thuộc về heuristic, nó có thể giúp sử dụng mã hóa các tệp đã nhận trước đó từ cùng một nguồn như một gợi ý đầu tiên.

Hầu hết mọi người (hoặc ứng dụng) đều thực hiện mọi thứ theo cùng một thứ tự mọi lúc, thường trên cùng một máy, do đó, rất có thể khi Bob tạo tệp .csv và gửi cho Mary, nó sẽ luôn sử dụng Windows-1252 hoặc bất cứ điều gì máy của anh ấy mặc định.

Trường hợp có thể một chút đào tạo khách hàng không bao giờ làm tổn thương :-)


0

Tôi thực sự đang tìm kiếm một cách chung chung, không phải lập trình để phát hiện mã hóa tập tin, nhưng tôi chưa tìm thấy điều đó. Những gì tôi đã tìm thấy bằng cách thử nghiệm với các bảng mã khác nhau là văn bản của tôi là UTF-7.

Vì vậy, nơi đầu tiên tôi đang làm: StreamReader file = File.OpenText (tên đầy đủ);

Tôi đã phải thay đổi nó thành: Tập tin StreamReader = new StreamReader (fullfilename, System.Text.Encoding.UTF7);

OpenText giả định đó là UTF-8.

bạn cũng có thể tạo StreamReader như StreamReader mới này (fullfilename, true), tham số thứ hai có nghĩa là nó sẽ thử và phát hiện mã hóa từ byteordermark của tệp, nhưng nó không hoạt động trong trường hợp của tôi.


@JohnMachin Tôi đồng ý rằng nó rất hiếm, nhưng nó là bắt buộc, ví dụ như trong một số phần của giao thức IMAP. Nếu đó là nơi bạn đang ở, bạn sẽ không phải đoán.
tripleee

0

Mở tệp trong AkelPad (hoặc chỉ sao chép / dán văn bản bị cắt xén), đi đến Chỉnh sửa -> Lựa chọn -> Mã hóa ... -> kiểm tra "Tự động phát hiện".


0

Là addon cho bài đăng ITmeze, tôi đã sử dụng chức năng này để chuyển đổi đầu ra của cổng C # cho Trình phát hiện bộ ký tự phổ quát của Mozilla

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

MSDN


0

Cảm ơn @ Erik Aronesty đã đề cập uchardet.

Trong khi đó, công cụ (giống?) Tồn tại cho linux : chardet.
Hoặc, trên cygwin bạn có thể muốn sử dụng : chardetect.

Xem: trang người đàn ông chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Điều này sẽ tự động phát hiện (đoán) mã hóa ký tự cho từng tệp đã cho và sẽ báo cáo tên và mức độ tin cậy cho mã hóa ký tự được phát hiện của mỗi tệp.


-1

Tôi sử dụng mã này để phát hiện Unicode và windows mặc định ansi codepage khi đọc tệp. Đối với các loại tiền mã hóa khác, việc kiểm tra nội dung là cần thiết, bằng tay hoặc bằng lập trình. Điều này có thể được sử dụng để lưu văn bản với cùng mã hóa như khi nó được mở. (Tôi sử dụng 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 (!) Đã qua vì điều này đã được hỏi, và tôi vẫn không thấy đề cập đến giải pháp tốt, không GPL'ed của MS: API IMultiL Language2 .

Hầu hết các thư viện đã được đề cập đều dựa trên UDE của Mozilla - và có vẻ hợp lý khi các trình duyệt đã giải quyết các vấn đề tương tự. Tôi không biết giải pháp của chrome là gì, nhưng vì IE 5.0 MS đã phát hành giải pháp của họ và đó là:

  1. Miễn phí các vấn đề cấp phép GPL và tương tự,
  2. Được hỗ trợ và duy trì có lẽ mãi mãi,
  3. Cung cấp đầu ra phong phú - tất cả các ứng cử viên hợp lệ cho mã hóa / mã hóa cùng với điểm tin cậy,
  4. Đáng ngạc nhiên là dễ sử dụng (đó là một cuộc gọi chức năng duy nhất).

Đó là một cuộc gọi COM bản địa, nhưng đây là một số công việc rất hay của Carsten Zeumer, xử lý các mớ hỗn độn để sử dụng .net. Có một số người khác xung quanh, nhưng nói chung, thư viện này không nhận được sự chú ý mà nó xứng đáng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.