长期以来,一直对字符串编码认识比较粗略,认为支持"特殊字符"编码就是Unicode。当然,.NET平台上很少需要考虑这类问题,但搞清一些基本概念还是很有好处的。

Unicode这个词,首先是国际标准的通用字符集(UCS)名称,囊括了汉语八国联军火星文等各种文字。这是一个面向用户的字符编码标准。其他的编码标准如GB2132,BIG5什么的都是Unicode标准之前的老黄历了,彼此间,与现代系统间各种不兼容。

而.Net中的UnicodeEncoding类,是实现Unicode字符集的一种编码方式,将一个字符转换成字节形式。其名称容易引起qi义,其实这个编码方式通用名称(在其他编程语言中)叫UTF16,此外流行的两种还有UTF8和UTF32,UTF8更常用。UTF是UCS Transformation Format的缩写。

这里也不贴具体的转换方式了,有兴趣可以到百科上去看。关键要知道.NET中UnicodeEncoding和UTF8Encoding两个类主要区别,前者每个字符一律都是两个字节,后者可能是1~3个字节:英文字母和标点1个,汉字占3个。显然这两种方式也不是兼容的,各有长短。

然而我们打开绝大多数文本文件,不管哪种编码,都可以正常显示所有内容。那么系统是如何判断文件的编码呢?我们可以做一个测试。

            var filename = "encoding-test";
var text = "变!";
var encodingInFile = Encoding.Unicode;
var encodingToCompare = Encoding.UTF8; //UTF16 encoding
Console.WriteLine("Saved with UTF16 Encoding:");
File.WriteAllText(filename, text, encodingInFile); Console.WriteLine("Read without Encoding: {0}",
File.ReadAllText(filename));
Console.WriteLine("Read with Encoding of File: {0}",
File.ReadAllText(filename, encodingInFile));
Console.WriteLine("Read with Another Encoding: {0}",
File.ReadAllText(filename, encodingToCompare));
Console.WriteLine();

可看到,无论是否指定,指定了何种编码去读取文本文件,都不影响系统识别正确的编码,输出结果都是"变!"。写入文件的Encoding换成UTF8等也一样,只要支持Unicode就可以。

但我们从字节上分析,就可以发现其中的区别,接着上面的代码,测试如下:

            var bytes = encodingInFile.GetBytes(text);
Console.WriteLine("Bytes from Text: {0}",
String.Join(" ", bytes.Select(b => b.ToString("X"))));
Console.WriteLine("Text from Bytes with File Encoding: {0}",
encodingInFile.GetString(bytes));
Console.WriteLine("Text from Bytes with Another Encoding: {0}",
encodingToCompare.GetString(bytes)); Console.WriteLine();
bytes = File.ReadAllBytes(filename);
Console.WriteLine("Bytes from File: {0}",
String.Join(" ", bytes.Select(b => b.ToString("X"))));
Console.WriteLine("Text from Bytes with File Encoding: {0}",
encodingInFile.GetString(bytes));
Console.WriteLine("Text from Bytes with Another Encoding: {0}",
encodingToCompare.GetString(bytes));

输出结果将是:

Bytes from Text: D8 53 21 0
Text from Bytes with File Encoding: 变!
Text from Bytes with Another Encoding: ?S!

Bytes from File: FF FE D8 53 21 0
Text from Bytes with File Encoding: ?变!
Text from Bytes with Another Encoding: ???S!

我们可以看到两点,一是字节数组转化字符串必须指定正确的Encoding;二是在文本文件中,系统在写入时多加了两个字节 FF FE,这就是系统能识别文件编码的关键。

这多余的两个字节,就是Unicode标准建议的用BOM(Byte Order Mark)。在写入传输表示文本的字节,先传输被作为BOM的字节以表明编码。对于UTF16或是UnicodeEncoding类,是FF FFE;对于UTF8则是EF BB BF,可以自己试一下。

所以在一般情况下,读取文本文件根本不必指定Encoding,必须指定Encoding是处理异常情况:该文件没有正确的保存,比如直接写入字节而不是文本,可能还有不少软件或系统会这么干,这样很容易造成读写错误而出现乱码。

要指出Excel就是这种我行我素的程序,对于CSV文件,要是用户想编辑输入Unicode字符,它会强迫你转换成xlsx格式,不然它连UTF8格式字节都不给你写入,用户重新打开后发现上次输入的Unicode字符全变成了问号。

另一个最常见的乱码原因是历史造成的。Unicode出现前,中韩日各种字符集百花乱放。这些旧字符集编码的文件,以及用了这种编码的程序,任何Unicode方式解码出来都是乱码,就像用密码加密一样。微软为解决这个问题,采取了页转换表作为过渡技术。在控制面板->区域语言选项->管理,可以找到“非Unicode程序中所使用的当前语言”,就是指定就种页表的。可以从Encoding.GetEncoding(int)取到,参数936是简体中文GBK,950是繁体中文BIG5。然而只能指定了一种非Unicode编码,如果还有电脑上还有别的非Unicode编码,那就只能任乱码横行了。

根本的解决之道,就是标准化,就是大家都用Unicode。至于选UTF8还是UTF16,看情况,对于中文文档,UTF16可以每个字节省一个字节。而国际上通用UTF8,也是XML标准编码。

弄清UTF8和Unicode的更多相关文章

  1. UTF-8和Unicode

    What's the difference between unicode and utf8? up vote 103 down vote favorite 49 Is it true that un ...

  2. 【GoLang】GoLang UTF8 与 Unicode

    结论: 通用的UTF8编码可是Ken Thompson和Rob Pike共同发明的, 他们都是Go的作者. Go中rune对应unicode的码点, string只是UTF8编码.len(" ...

  3. [转]unicode,ansi,utf-8,unicode big endian的故事

    unicode,ansi,utf-8,unicode big endian的故事很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的 ...

  4. paip.utf-8,unicode编码的本质输出unicode文件原理 python

    paip.utf-8,unicode编码的本质输出unicode文件原理 python      #别的语言,java php都是unicode,走十python不一样.    #enddef  #t ...

  5. PHP UTF-8和Unicode编号互转

    PHP UTF-8和Unicode编号互转 /** * utf-8 转unicode * * @param string $name * @return string */ function utf8 ...

  6. 跨平台utf8转unicode研究实现(2)

    最近在用VC++开发一个小工具,平时用惯了.NET,用起VC++最郁闷的就是字符串处理.当然最最让人难于琢磨的就是字符集,编码之间的转换.通过这几天的研究,终于明白了Unicode和UTF-8之间编码 ...

  7. 浅谈编码Base64、Hex、UTF-8、Unicode、GBK等

    网络上大多精彩的回答,该随笔用作自我总结: 首先计算机只认得二进制,0和1,所以我们现在看到的字都是经过二进制数据编码后的:计算机能针对0和1的组合做很多事情,这些规则都是人定义的:然后有了字节的概念 ...

  8. linux下php中文UTF-8转换Unicode方法和注意事项

    先说下遇到问题:1.php没有内置unicode_ecode函数可以直接使用 2.网上很多资料都是用$str = iconv($encoding, 'UCS-2', $str); window下转换出 ...

  9. utf-8和Unicode的区别

    链接 utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念 要想先讲清楚他们的区别,首先应该讲讲Unicode的来由. 众 ...

随机推荐

  1. 时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现

    本文首发蛮牛,次发博客园.接系列 第一篇,第二篇,本文为第三篇,再次感谢“武装三藏”在前两篇无私且精彩的问题解答 写在最前,时光煮雨,为了怀念 以下引用曾今读过的一些教程文章 其实这3种动画都有它特定 ...

  2. 安装django

    我已经有Python3.5的环境了.我们去下载Django.https://github.com/django/django.git  直接下载为zip解压即可. 然后在命令提示符下安装 1.  切换 ...

  3. h5移动端前端性能优化

    1.脚本优化 (1)减少重绘和回流 (2)缓存Dom选择与计算 (3)缓存列表length (4)尽量使用事件代码,避免批量绑定事件 (5)尽量使用ID选择器 (6)使用touchstart.touc ...

  4. TypeError: unsupported operand type(s) for |: 'str' and 'str'

    问题描述:

  5. PHP Socket 编程过程详解

    使用代码 目的:开发一个客户端用于发送string消息到服务端,服务端将相同的信息反转后返回给客户端. PHP服务器 第1步:设置变量,如“主机”和“端口” $host = "127.0.0 ...

  6. [转]http://m635674608.iteye.com/blog/1750833

    转载地址:http://m635674608.iteye.com/blog/1750833 在第一次使用TortoiseSVN从服务器CheckOut的时候,会要求输入用户名和密码,这时输入框下面有个 ...

  7. 对于JQuery的一些见解

    jQuery是什么?(了解)  www.github.com jQuery 其实就是一堆的js函数,是普通的js,只不过应用广泛,形成了行业标准. 参考书:锋利的jQuery 学习参考:http:// ...

  8. oracle分区提高篇

      一. 分区表理论知识 Oracle提供了分区技术以支持VLDB(Very Large DataBase).分区表通过对分区列的判断,把分区列不同的记录,放到不同的分区中.分区完全对应用透明. Or ...

  9. iotop命令

    简介: iotop – simple top-like I/O monitor iotop是一个用来监视磁盘I/O使用状况的 top 类工具,可监测到哪一个程序使用的磁盘IO的信息(requires ...

  10. [问题2014S15] 复旦高等代数II(13级)每周一题(第十五教学周)

    [问题2014S15]  设 \(O\) 为 \(n\) 阶正交阵,\(A=\mathrm{diag}\{a_1,a_2,\cdots,a_n\}\) 为实对角阵, 证明: 方阵 \(OA\) 的特征 ...