---恢復內容開始---
在遙遠的2008年9月18日, 網友@ GvS 在stackoverflow上提了這么一個問題:
“如何檢測文本文件的編碼/內碼頁?”
“在我們的應用程序中,會接收來自不同來源的文本文件(.txt, .csv等)。程序讀取時,這些文件有時會包含垃圾,因為它們是使用不同的/未知的’內碼頁’創建的。”
“有沒有辦法(自動)檢測文本文件的內碼頁呢?”
第一位回答的網友@ JV 是這樣說的:
“你無法檢測‘內碼頁’,你需要被告知使用的‘內碼頁’是什么。”
“你可以分析文本的字節並猜測它,但可能會帶來一些奇怪的(滑稽的)的結果。”
“我沒有辦法自動檢測,但我肯定notepad可以被誘騙到顯示中文的英文文本。”
“無論如何,你需要閱讀的內容:
絕對是每個軟件開發人員最低限度的,必須積極了解的Unicode和字符集.”
http://www.joelonsoftware.com/articles/Unicode.html
“具體一點,Joel說:關於編碼的一個最重要的事實”
‘如果你完全忘記了我剛才解釋的一切,請記住一個非常重要的事實。在不知道使用哪種編碼的情況下生成一個字符串是沒有意義的。你不能再將頭伸進沙子里,假裝“簡單”文本是ASCII。 沒有像純文本那樣的東西。’
“如果你有一個字符串,在內存中也好,在文件中也好,或者是電子郵件中的信息也好,你必須知道它的編碼方式,否則無法正確的解釋或者顯示給用戶”。
第二位網友@Tomer Gabel 沒有直面問題而是寫道:
“如果希望檢測非UTF編碼(即沒有BOM),那么基本上就是文本的啟發式(heuristics)和統計(statistical )分析。”
“你可能想看看關於通用字符集檢測的Mozilla論文。”
http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html
針對這個回答,@Tao 糾正到:
“你說的 ‘如果你想檢測非UTF編碼(即沒有BOM)’有點誤導; unicode標准不建議向utf-8文檔添加BOM! (並且這個建議或者缺乏這個建議是許多令人頭疼的根源)。ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8 ”
@ ITmeze 直截了當的給出來c#的方案:
“你是否嘗試過 C# port for Mozilla Universal Charset Detector”
“例子來源於: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."); } } }
@ shoosh 認為內碼頁可以檢測到
“ ’無法檢測到內碼頁’
這顯然是錯誤的。 每個網頁瀏覽器都有某種通用字符集檢測器來處理沒有任何編碼指示的頁面。 Firefox有一個。 你可以下載代碼,看看它是如何做到的。 文檔在這里。 基本上,這是一種啟發式的方法,但其效果非常好。
http://www.mozilla.org/projects/intl/detectorsrc.html
給定合理數量的文本,甚至可以檢測到該語言。
這是我剛剛使用Google發現的另一個”
http://chardet.feedparser.org/
但@ JV 進行了調侃: “啟發式” - 所以瀏覽器沒有完全檢測到它,這是一個有根據的猜測。 “工作得很好” - 所以不是一直都工作? 聽起來我們好像達成了一致
@TarmoPikaro 同學也找到了https://code.google.com/p/ude/ 這個解決方案,但覺得有點笨重:
“我需要一些基本的編碼檢測,基於4個第一字節和可能的xml字符集檢測 - 所以我從互聯網上拿了一些示例源代碼並稍微地修改為以下(Java版本)。”
http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html
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 }
“從文件讀取可能的前1024個字節就足夠了,但是我加載了整個文件。”
@Erik Aronesty 同學給出了在Ubuntu下的解決方案:
“工具‘uchardet’為每個字符集使用字符頻率分布模型,所以工作的很好。”
“大文件和‘典型’文件,效果更好。”
“在Ubuntu上,通過命令: apt-get install uchardet”
“其它系統,可以在 https://github.com/BYVoid/uchardet 獲取源碼、用戶和文檔。”
這個答案比較有意思,因為在Related Projects中,囊括了Python\Ruby\Java\C#\Rust\C++\C等相關項目。哦這個項目本身也是c/c++的。
原文:https://stackoverflow.com/questions/90838/how-can-i-detect-the-encoding-codepage-of-a-text-file/5830273#5830273