长期以来,一直对字符串编码认识比较粗略,认为支持"特殊字符"编码就是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. 使用Go开发web服务器

    原文链接 Go(Golang.org)是在标准库中提供HTTP协议支持的系统语言,通过他可以快速简单的开发一个web服务器.同时,Go语言为开发者提供了很多便利.这本篇博客中我们将列出使用Go开发HT ...

  2. 【java开发系列】—— struts2简单入门示例

    前言 最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的. struts2其实就是为我们封装了servlet,简化了js ...

  3. Adobe CS6 全系列官方下载地址 (迅雷无效) Win Mac

    https://helpx.adobe.com/x-productkb/policy-pricing/cs6-product-downloads.html CS6 Design & Web P ...

  4. VBA好的插件

    VBE 小插件--- Office 编程助手 :http://club.excelhome.net/thread-1055425-1-1.html VB以及VBA的编程助手MZ-tools7.0破解版 ...

  5. java Jsoup 抓取页面数据

    List<ImageBean> imgList = new ArrayList<ImageBean>(); ImageBean image = null; String ima ...

  6. mysql常用命令(3)

    一.启动与关闭 1.1 Linux下启动mysql 的命令: a. rpm包安装:service mysqld start b. 源码包安装:/usr/local/mysql/bin/mysqld_s ...

  7. Linux C Programing - Arguments(2)

    #include <iostream> #include <stdlib.h> #include <stdio.h> //printf #include <u ...

  8. cocos2d-x的lua脚本加载CocostudioUI两种方式

    前言 当前版本使用的是quick cocos2dx lua 3.3.UI使用cocostudio编辑器1.6.0.我们在程序里面可以使用两种方式进行解析UI.开始的时候用的是quick的方法, 结果遇 ...

  9. tas5721 驱动

    Submitter Mark Brown Date March 31, 2016, 5:33 p.m. Message ID <E1algTO-0004Cm-Ga@finisterre> ...

  10. 2016年12月25日 星期日 --出埃及记 Exodus 21:20

    2016年12月25日 星期日 --出埃及记 Exodus 21:20 "If a man beats his male or female slave with a rod and the ...