关于Encoding.GetEncoding("utf-8")和Encoding.GetEncoding("GB2312")及Encoding.Default

最近处理bs的GridView导出word、excel的时候,经常出现乱码的问题,一直很头疼,折腾了两天,从网上了找到了一些资料,其实说到底就是换成byte[]或者字节流最后输入的问题。

根据多次在ie6、ie7上测试发现用Encoding.Default的基本可以解决95%的乱码输出的问题,虽然网上有帖子说“谨慎使用Encoding.Default”,原帖见备注一

但是另外的5%的乱码需要Encoding.GetEncoding("utf-8")才能解决,至于Encoding.GetEncoding("GB2312")没测试,因为尚未发现Encoding.GetEncoding("utf-8")和Encoding.Default都无法解决的境况。UTF-8和GB2312的区别在备注二中。

这样的话,治本的方法就是上传或下载导出文件时,分析上传文件字节流需要知道它的编码,不过C#目前还没有现成的函数能够获取,具体的方法可以参见备注三。

但是治本的方法也有缺点,1.对服务器的处理需求增大 2.修要修改的工作量增加。我这有个治标的办法,就是让user选择编码。虽然我极力不想这么做,但是没办法,学费还是要交的,下一版的时候注意。

备注四中有Encoding.Default的讲解。

 

 

备注一:from http://hi.baidu.com/irinihp/blog/item/d196cd35b4e7cb83a61e12aa.html

谨慎使用Encoding.Default

2009-03-30 15:36

 

在处理文本时,经常需要处理Encoding的概念。存在这个问题的原因在于,在.NET程序内文本只是string和char对象,而保存到硬盘时,或者准备用socket把它发送出去时,都得先转换成byte[]或者字节流。而转换的算法,就是Encoding。

老外的程序里面,都喜欢用Encoding.ASCII,这是因为他们主要只使用western字符;于是我们经常要在他们的代码把Encoding.ASCII替换成Encoding.Default才能使用。

所谓Encoding.Default,是指当前系统设置的“默认字符集编码方式”。你可以通过控制面板里面的区域选项设置它(这是在我的机器上的设定):

查看图片

注意红色方框内的部分,“为你的非Unicode程序选择一个语言来匹配其本地语言版本”。这里选择了Chinese (PRC),则Encoding.Default等效于Encoding.GetEncoding("gb2312")。gb2312在代码页936,所以所有以双字节编码(ANSI编码)字符的程序在这个系统上会使用936代码页,使用Unicode的不受影响。

你可以看到 Encoding.Default的值是和系统设定相关的。这样,有些时候会出问题:一台机器用socket发送一段文本到另一台机器,两边都设定编码方式为Encoding.Default,但两台机器的区域选项的这个设置是不一样的,却没有被注意;如果发送的是非western字符,则接受方必然会得到乱码。这种问题往往会令人很困惑。

所以在面对中文且为ANSI编码的情况下,最好不要用Encoding.Default,而用 Encoding.GetEncoding("gb2312"),或者更直接的Encoding.GetEncoding(936);而如果有可能的话,最好全部使用unicode,比如utf-8,也就是Encoding.UTF8。有了unicode之后,其实我们不需要代码页的概念。

 

 

备注二:http://blog.csdn.net/forsiny/archive/2009/11/15/4813107.aspx

C#文本文件编码问题,区别UTF-8和GB2312 收藏


最近用C#涉及到一些读取 txt文本文件的操作,但是一个编码问题就困惑了我好久。如果编码选的不对,会造成乱码。之前转载的一片文章提出了一种解决方法,就是用new StreamReader(file, Encoding.Default)。这种方法解决了大部分问题,但是测试中发现对于有的UTF-8文件依然会造成乱码(中文windows环境)。

于是上网搜索解决方案。大多数是说UTF-8有特殊的前导码EF BB BF,只要认出这个就能判定是UTF-8编码了。但是我测试的一个文件发现前面并

 没有这些前导码啊…于是继续搜索……

先转一篇直接知道怎么做的博文:

http://blog.csdn.net/zdg/archive/2005/01/29/272643.aspx


--------------------------------------------------------------------------------

 一)需求
很多情况下我们需要知道字节流的编码,比如
1) 使用编辑器打开文本文件的时候,编辑器需要识别文本文件的各种编码
2) 上传文件后,分析上传文件字节流需要知道它的编码

二)探讨
不过C#目前还没有现成的函数能够获取,经过和同事的探讨,发现UTF8文件都有一个3字节的头,为“EF BB BF”(称为BOM--Byte Order Mark),判断这个头信息不就可以解决了吗?代码如下:

//判断上传的文件的编码是否是UTF8,buff为上传文件的字节流
    enc     = Encoding.UTF8;
    testencbuff   = enc.GetPreamble();
    if(fileLength>testencbuff.Length && testencbuff[0] == buff[0] && testencbuff[1]==buff[1] && testencbuff[2]==buff[2])
    {
        // 是 UTF8编码
        string buffString  = enc.GetString(buff);
    }
不过后来发现,不是所有的UTF8编码的文件都有BOM信息,那如何解决呢?

三)最终的方案
没有BOM信息只有通过逐个字节比较的方式才能解决。幸好已经有人解决这个问题了。推荐大家看:
http://dev.csdn.net/Develop/article/10/10961.shtm
http://dev.csdn.net/Develop/article/10/10962.shtm
这里判断所有的编码,基本上都是通过字节比较的方式。java代码很容易移植到.NET上,下面是UTF8判断部分的C#代码:

  int utf8_probability(byte[] rawtext)
  {
   int score = 0;
   int i, rawtextlen = 0;
   int goodbytes = 0, asciibytes = 0;

   // Maybe also use UTF8 Byte Order Mark:  EF BB BF

   // Check to see if characters fit into acceptable ranges
   rawtextlen = rawtext.Length;
   for (i = 0; i < rawtextlen; i++)
   {
    if ((rawtext[i] & (byte)0x7F) == rawtext[i])
    {  // One byte
     asciibytes++;
     // Ignore ASCII, can throw off count
    }
    else
    {
     int m_rawInt0 = Convert.ToInt16(rawtext[i]);
     int m_rawInt1 = Convert.ToInt16(rawtext[i+1]);
     int m_rawInt2 = Convert.ToInt16(rawtext[i+2]);

     if (256-64 <= m_rawInt0 && m_rawInt0 <= 256-33 && // Two bytes
      i+1 < rawtextlen &&
      256-128 <= m_rawInt1 && m_rawInt1 <= 256-65)
     {
      goodbytes += 2;
      i++;
     }
     else if (256-32 <= m_rawInt0 && m_rawInt0 <= 256-17 && // Three bytes
      i+2 < rawtextlen &&
      256-128 <= m_rawInt1 && m_rawInt1 <= 256-65 &&
      256-128 <= m_rawInt2 && m_rawInt2 <= 256-65)
     {
      goodbytes += 3;
      i+=2;
     }
    }
   }

   if (asciibytes == rawtextlen) { return 0; }

   score = (int)(100 * ((float)goodbytes/(float)(rawtextlen-asciibytes)));

   // If not above 98, reduce to zero to prevent coincidental matches
   // Allows for some (few) bad formed sequences
   if (score > 98)
   {
    return score;
   }
   else if (score > 95 && goodbytes > 30)
   {
    return score;
   }
   else
   {
    return 0;
   }

  }

 

--------------------------------------------------------------------------------


OK.利用上面的代码,我来判断一下是UTF-8编码的概率:

 view plaincopy to clipboardprint?
Encoding encode;  
StreamReader srtest = new StreamReader(file.FullName,Encoding.Default);  
int p = utf8_probability(Encoding.Default.GetBytes(srtest.ReadToEnd()));  
if( p>80 )  
    encode = Encoding.GetEncoding(65001);//utf8  
else 
    encode = Encoding.Default;  
srtest.Close(); 
Encoding encode;
StreamReader srtest = new StreamReader(file.FullName,Encoding.Default);
int p = utf8_probability(Encoding.Default.GetBytes(srtest.ReadToEnd()));
if( p>80 )
    encode = Encoding.GetEncoding(65001);//utf8
else
    encode = Encoding.Default;
srtest.Close();

大功告成~哈哈~

感谢zdg的博文~

 

 

备注三:from http://blog.csdn.net/zdg/archive/2005/01/29/272643.aspx

字节流编码获取原来这么复杂 收藏
一)需求
很多情况下我们需要知道字节流的编码,比如
1) 使用编辑器打开文本文件的时候,编辑器需要识别文本文件的各种编码
2) 上传文件后,分析上传文件字节流需要知道它的编码

二)探讨
不过C#目前还没有现成的函数能够获取,经过和同事的探讨,发现UTF8文件都有一个3字节的头,为“EF BB BF”(称为BOM--Byte Order Mark),判断这个头信息不就可以解决了吗?代码如下:

//判断上传的文件的编码是否是UTF8,buff为上传文件的字节流
    enc     = Encoding.UTF8;
    testencbuff   = enc.GetPreamble();
    if(fileLength>testencbuff.Length && testencbuff[0] == buff[0] && testencbuff[1]==buff[1] && testencbuff[2]==buff[2])
    {
        // 是 UTF8编码
        string buffString  = enc.GetString(buff);
    }
不过后来发现,不是所有的UTF8编码的文件都有BOM信息,那如何解决呢?

三)最终的方案
没有BOM信息只有通过逐个字节比较的方式才能解决。幸好已经有人解决这个问题了。推荐大家看:
http://dev.csdn.net/Develop/article/10/10961.shtm
http://dev.csdn.net/Develop/article/10/10962.shtm
这里判断所有的编码,基本上都是通过字节比较的方式。java代码很容易移植到.NET上,下面是UTF8判断部分的C#代码:

  int utf8_probability(byte[] rawtext)
  {
   int score = 0;
   int i, rawtextlen = 0;
   int goodbytes = 0, asciibytes = 0;

   // Maybe also use UTF8 Byte Order Mark:  EF BB BF

   // Check to see if characters fit into acceptable ranges
   rawtextlen = rawtext.Length;
   for (i = 0; i < rawtextlen; i++)
   {
    if ((rawtext[i] & (byte)0x7F) == rawtext[i])
    {  // One byte
     asciibytes++;
     // Ignore ASCII, can throw off count
    }
    else
    {
     int m_rawInt0 = Convert.ToInt16(rawtext[i]);
     int m_rawInt1 = Convert.ToInt16(rawtext[i+1]);
     int m_rawInt2 = Convert.ToInt16(rawtext[i+2]);

     if (256-64 <= m_rawInt0 && m_rawInt0 <= 256-33 && // Two bytes
      i+1 < rawtextlen &&
      256-128 <= m_rawInt1 && m_rawInt1 <= 256-65)
     {
      goodbytes += 2;
      i++;
     }
     else if (256-32 <= m_rawInt0 && m_rawInt0 <= 256-17 && // Three bytes
      i+2 < rawtextlen &&
      256-128 <= m_rawInt1 && m_rawInt1 <= 256-65 &&
      256-128 <= m_rawInt2 && m_rawInt2 <= 256-65)
     {
      goodbytes += 3;
      i+=2;
     }
    }
   }

   if (asciibytes == rawtextlen) { return 0; }

   score = (int)(100 * ((float)goodbytes/(float)(rawtextlen-asciibytes)));

   // If not above 98, reduce to zero to prevent coincidental matches
   // Allows for some (few) bad formed sequences
   if (score > 98)
   {
    return score;
   }
   else if (score > 95 && goodbytes > 30)
   {
    return score;
   }
   else
   {
    return 0;
   }

  }

参考资料:
字符检测程序(上) 检测GB2312、BIG5...    
http://dev.csdn.net/Develop/article/10/article/10/10961.shtm
Hello Unicode ——JAVA的中文处理学习笔记
http://www.chedong.com/tech/hello_unicode.html

 

 

备注四: from http://m.cnblogs.com/3108/768062.html

今天解决了一个棘手的问题。
在Mono下面,StreamReader用Encoding.Default竟然无法正常读取GBK编码的文件。

随后展开了调查,用GBK编码的源代码中文Hello world程序竟然输出乱码!
用UTF-8编码的源代码文件编译的程序就是正确的。

难道真都是Mono对编码支持很混乱的原因吗?

不!很多.NET程序员把Encoding.Default理解错了。因为在Windows平台上Encoding.Default确实等于“GB18030”也就是GBK。

但是,随着环境的不同,Encoding.Default也会改变!比如在WinCE或者是一部分Linux,Unix上,默认的编码就是UTF-8,
这时候,Encoding.Default就相当于是Encoding.UTF8!

那要如何在默认是UTF-8的平台读取GBK编码的文件呢?很简单,用Encoding.GetEncoding("GBK")就可以了,GBK兼容GB2312。

以上是个人拙见,欢迎批评指正。

PS:
Oh,yeah yeah,我知道有些人会说,如果在Windows上就没有这样的烦心事,Mono的C# complier应该自动处理源文件编码的问题(针对
于关于非默认编码的源文件的问题)。很不幸的告诉你,.NET Framework自带的C#编译器同样不能正确处理非默认编码的源文件,比如
用Western什么什么的编码的源文件,特殊字符甚至会导致编译失败。但是为什么在Windows下,UTF-8编码的源文件就可以正确处理呢(
在UTF-8不是操作系统默认的编码的情况下)?说实话,that's a little tricky,因为绝大多数的UTF-8的编码都是带有BOM的,很多人应
该还对VS2003中ASP.NET使用UTF-8编码的源文件造成浏览时候变成乱码记忆犹新吧,那就是因为VS2003默认的UTF-8编码是没有BOM的!
导致C#编译起编译ASP.NET页面的时候错误的使用了默认的GBK的编码,导致了最终页面的乱码现象。好在现在VS2005默认的UTF-8都也已
经是UTF-8 with signature了。哦,BOM的全称是Byte Ordered Mask,目的是为了区别Unicode big或者small endian的,后来被一些Geek
用来区别是不是UTF-8了,当然,这样做有利也有弊,总的来说,不够elegant。

你可能感兴趣的:(Web技术,javascript,编辑器)