2008-09-18 64 views
270

在我们的应用程序中,我们收到来自不同来源的文本文件(.txt.csv等)。在阅读时,这些文件有时会包含垃圾,因为这些文件是在不同的/未知的代码页中创建的。如何检测文本文件的编码/代码页

有没有办法(自动)检测文本文件的代码页?

detectEncodingFromByteOrderMarks,在StreamReader构造,适用于UTF8和其他的Unicode标文件,但是我正在寻找一种方法来检测代码页,像ibm850windows1252


感谢您的回答,这就是我所做的。

我们收到的文件来自最终用户,他们没有关于代码页的线索。接收者也是最终用户,现在这是他们对代码页的了解:代码页存在并且很烦人。

解决方案:

  • 用记事本打开接收的文件,看一个乱码一段文字。如果有人被称为弗朗索瓦什么的东西,用你的人类智慧你可以猜出这个。
  • 我创建了一个小应用程序,用户可以使用该应用程序打开该文件,并在使用正确的代码页时输入用户知道该文件将出现在文件中的文本。
  • 遍历所有代码页,并显示给出用户提供的文本的解决方案。
  • 如果弹出更多的代码页,请让用户指定更多文本。

回答

249

你无法检测到代码页,你需要被告知它。你可以分析字节并猜测它,但是这可能会导致一些奇怪的(有时候很有趣)的结果。我现在找不到它,但我确信记事本可以被诱骗显示中文的英文文本。

无论如何,这是你需要阅读的内容: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

具体乔尔说:

的一个最重要的事实,关于编码

如果你完全忘记了一切,我刚才解释,请记住一个非常重要的事实。在不知道使用哪种编码的情况下生成一个字符串是没有意义的。你不能再将头伸进沙子里,假装“简单”文本是ASCII。 不过是没有这样的东西作为纯文本。

如果你有一个字符串,在内存中,在文件中,或者在电子邮件中,你必须知道什么编码它是在或无法解释或将它正确地显示给用户。

+76

找到了它:http://en.wikipedia.org/wiki/Bush_hid_the_facts – 2008-09-18 08:41:25

3

StreamReader类的构造函数接受'detect encoding'参数。

+0

这只是“编码”[链接](https://msdn.microsoft.com/en-us/library/system.io.streamreader%28v=vs.110%29.aspx)这里..和描述说我们必须提供编码.. – SurajS 2015-03-09 06:36:53

+0

@SurajS:看看其他重载。 – leppie 2015-03-09 06:45:34

+0

原作者想要检测文件的编码,该文件可能没有BOM标记。 StreamReader根据签名检测来自BOM表头的编码。 公众的StreamReader( \t流流, \t布尔detectEncodingFromByteOrderMarks ) – ibondre 2015-08-14 01:16:49

30

如果你想检测非UTF编码(即没有BOM),你基本上是对文本的启发式和统计分析。您可能需要查看Mozilla paper on universal charset detectionsame link, with better formatting via Wayback Machine)。

+8

有趣的是我的Firefox 3.05安装检测页面为UTF-8,可见一些问号-IN-A-钻石字形,尽管源具有meta-tag for Windows-1252。手动更改字符编码可正确显示文档。 – devstuff 2008-12-29 19:45:30

+5

您的句子“如果您希望检测非UTF编码(即没有BOM)”稍有误导性; unicode标准不建议向utf-8文档添加BOM! (并且这个建议或者缺乏这个建议是许多令人头疼的根源)。 ref:http://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 – Tao 2011-05-19 09:35:12

+0

这样做是为了在不累积冗余BOM的情况下连接UTF-8字符串。此外,UTF-8不需要字节顺序标记,例如与UTF-16不同。 – sashoalm 2013-08-14 08:17:41

0

得到了同样的问题,但没有找到一个好的解决方案,但自动检测它。 现在即时使用PsPad(www.pspad.com);)工作正常

4

我已经做在Python类似的东西。基本上,你需要大量的从各种编码,这是通过滑动两个字节的窗口分解并存储在字典(散列)的样本数据,键连接在字节对编码提供的列表中的值的。

鉴于字典(散),你把你的输入文字和:

  • ,如果它与任何BOM字符( '\ XFE \ XFF' 为UTF-16-BE,“\ XFF \ XFE开始“为UTF-16-LE,‘\ XEF \ XBB \ XBF’为UTF-8等),我把它当作建议
  • 如果没有,那么把文本的足够大的样本,采取一切字节对样本并选择字典中建议的最不常用的编码。

如果你还采样UTF编码的文本是做开始与任何BOM,第二步将覆盖那些从第一步下滑。

到目前为止,它为我工作(样本数据和后续的输入数据是各种语言的字幕)与减少错误率。

16

不能检测编码页

这显然是错误的。每个网页浏览器都有某种通用字符集检测器来处理没有任何编码指示的页面。 Firefox有一个。你可以下载代码,看看它是如何做到的。请参阅一些文档here。基本上,这是一种启发式的方法,但其效果非常好。

给定文本的合理数量的,甚至有可能检测到的语言。

Here's another one我只是使用谷歌发现:

0

由于它基本上可以归结为启发式,它可以帮助使用先前接收到的文件的编码从同一来源的第一个暗示。

大多数人(或应用)做的东西在几乎相同的顺序每一次,经常在同一台机器上,所以它很可能是当Bob创建一个.csv文件,并将其发送给玛丽,它会始终使用Windows-1252或他的机器默认的任何设备。

如果可能的话有点客户培训从来没有伤害任何:-)

0

我其实是在寻找的检测文件编码的通用,而不是编程方式,但我没有发现呢。 我也用不同的编码测试发现的是,我的文字是UTF-7。

所以在这里我先在做: StreamReader的文件=文件。OpenText的(fullfilename);

我不得不将它更改为: StreamReader file = new StreamReader(fullfilename,System.Text.Encoding.UTF7);

OpenText的假定它是UTF-8。

你也可以创建这样 新的StreamReader(fullfilename,真),第二个参数意味着它应该尝试从文件的byteordermark检测的编码,但没有我的情况下工作的StreamReader。

8

我知道这个问题已经很晚了,这个解决方案不会吸引某些人(因为它以英语为中心的偏见和缺乏统计/经验测试),但它对我来说非常好,特别是对于处理上传的CSV数据:

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

优点:

  • BOM检测内置
  • 默认/备用编码定制
  • 相当可靠(在我的经验),用于容纳用UTF-8和Latin-1的样式文件的混合异国情调的数据(例如法国名字)的西欧式为基础的文件 - 基本上大部分美国和西欧的环境。

注意:我是写这门课的人,所以很明显带上一粒盐! :)

-2

我用这个代码来检测Unicode和Windows默认的ANSI代码页读取文件时。对于其它值编码的内容的检查是必要的,手动地或通过编程。这可以去用文字保存相同的编码它被打开时。 (我用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() 
19

你从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."); 
     } 
    } 
}  
7

寻找不同的解决方案,我发现

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

这个解决方案是有点重。

我需要一些基本的编码检测的基础上,4个字节的第一和可能的XML字符集探测 - 所以我从网上花了一些示例源代码并添加略作修改的

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

版本写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 
    } 

这足以从文件中读取可能首先1024个字节,但我加载整个文件。

2

工具“uchardet”很好地利用每个字符集的字符频率分布模型。较大的文件和更多的“典型”文件有更多的信心(显然)。

在Ubuntu上,你只需apt-get install uchardet

在其他系统上,获取源,使用&文档浏览:https://github.com/BYVoid/uchardet

0

至于附加到ITmeze后,我用这个函数C#端口的输出转换为Mozilla通用字符检测

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

MSDN

5

如果有人正在寻找一个93.9%的溶液。这个工作对我来说:

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

打开文件AkelPad(或只是复制/粘贴乱码),去编辑 - >选择 - >重新编码 - >勾选“自动检测”。