长期以来,一直对字符串编码认识比较粗略,认为支持"特殊字符"编码就是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. Struts2(五):ActionSupport

    我们在上一章节中的一个列子中使用到了一个标识跳转到登录页面的例子: 示例是这样写的: index.jsp: <br/> <a href="gotoLoginPage&quo ...

  2. Scala 流程空间,函数,异常处理

    1,)首先留意一下下边的代码块,他是怎么运行的,貌似在c#中他是出错的,不应该出现这样的写法的,但在scala中侧不然: package com.dt.study /** * The package ...

  3. Latex 学习

    Latex 学习 @(05.2 Latex)[latex][基础教程] 这篇文章是关于latex基础教程.pdf的一个总结. 基本知识 基础 1. 空格 多个空格会被视为一个空格 单个回车会被视为一个 ...

  4. ligerui_ligerTree_004_对"ligerTree"节点操作

    ligerTree节点操作: 源码地址:http://download.csdn.net/detail/poiuy1991719/8571255 效果图: 代码: json.txt: [ { text ...

  5. Linux内核内存管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298718 版权声明:本文为博主原创文章,未经博主允许不得转载. 还是那张熟悉 ...

  6. SQL标签

    SQL标签库提供了与关系型数据库进行交互的标签. 引入语法:<%@ taglib prefix="sql" uri="http://java.sun.com/jsp ...

  7. php计算两个日期相差 年 月 日

    在PHP程序中,很多时候都会遇到处理时间的问题,比如:判断用户在线了多长时间,共登录了多少天,两个帖子发布的时间差或者是不同操作之间的日志记录等等.在文章中,简单地举例介绍了PHP中如何计算两个日期相 ...

  8. Abot爬虫和visjs

    1. 引言 最近接触Abot爬虫也有几天时间了,闲来无事打算从IMDB网站上爬取一些电影数据玩玩.正好美国队长3正在热映,打算爬取漫威近几年的电影并用vis这个JS库呈现下漫威宇宙的相关电影. Abo ...

  9. 关于ORACLE隐式转换后性能问题

    SELECT TM.MONEY_CODE FROM T_CONTRACT_MASTER T,T_MONEY TM WHERE T.MONEY_ID = TM.MONEY_ID AND T.POLICY ...

  10. wf(四)

    我们已经在c#和xaml上编写了工作流,或者有的人会觉得在xaml上编写的workflow没什么优点,然而其实xaml其实具有一些很特别的优势. 1. xaml支持工作流设计器,c#不支持: 2. x ...