弄清UTF8和Unicode
长期以来,一直对字符串编码认识比较粗略,认为支持"特殊字符"编码就是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的更多相关文章
- UTF-8和Unicode
What's the difference between unicode and utf8? up vote 103 down vote favorite 49 Is it true that un ...
- 【GoLang】GoLang UTF8 与 Unicode
结论: 通用的UTF8编码可是Ken Thompson和Rob Pike共同发明的, 他们都是Go的作者. Go中rune对应unicode的码点, string只是UTF8编码.len(" ...
- [转]unicode,ansi,utf-8,unicode big endian的故事
unicode,ansi,utf-8,unicode big endian的故事很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的 ...
- paip.utf-8,unicode编码的本质输出unicode文件原理 python
paip.utf-8,unicode编码的本质输出unicode文件原理 python #别的语言,java php都是unicode,走十python不一样. #enddef #t ...
- PHP UTF-8和Unicode编号互转
PHP UTF-8和Unicode编号互转 /** * utf-8 转unicode * * @param string $name * @return string */ function utf8 ...
- 跨平台utf8转unicode研究实现(2)
最近在用VC++开发一个小工具,平时用惯了.NET,用起VC++最郁闷的就是字符串处理.当然最最让人难于琢磨的就是字符集,编码之间的转换.通过这几天的研究,终于明白了Unicode和UTF-8之间编码 ...
- 浅谈编码Base64、Hex、UTF-8、Unicode、GBK等
网络上大多精彩的回答,该随笔用作自我总结: 首先计算机只认得二进制,0和1,所以我们现在看到的字都是经过二进制数据编码后的:计算机能针对0和1的组合做很多事情,这些规则都是人定义的:然后有了字节的概念 ...
- linux下php中文UTF-8转换Unicode方法和注意事项
先说下遇到问题:1.php没有内置unicode_ecode函数可以直接使用 2.网上很多资料都是用$str = iconv($encoding, 'UCS-2', $str); window下转换出 ...
- utf-8和Unicode的区别
链接 utf-8和Unicode到底有什么区别?是存储方式不同?编码方式不同?它们看起来似乎很相似,但是实际上他们并不是同一个层次的概念 要想先讲清楚他们的区别,首先应该讲讲Unicode的来由. 众 ...
随机推荐
- 使用Go开发web服务器
原文链接 Go(Golang.org)是在标准库中提供HTTP协议支持的系统语言,通过他可以快速简单的开发一个web服务器.同时,Go语言为开发者提供了很多便利.这本篇博客中我们将列出使用Go开发HT ...
- 【java开发系列】—— struts2简单入门示例
前言 最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的. struts2其实就是为我们封装了servlet,简化了js ...
- Adobe CS6 全系列官方下载地址 (迅雷无效) Win Mac
https://helpx.adobe.com/x-productkb/policy-pricing/cs6-product-downloads.html CS6 Design & Web P ...
- VBA好的插件
VBE 小插件--- Office 编程助手 :http://club.excelhome.net/thread-1055425-1-1.html VB以及VBA的编程助手MZ-tools7.0破解版 ...
- java Jsoup 抓取页面数据
List<ImageBean> imgList = new ArrayList<ImageBean>(); ImageBean image = null; String ima ...
- mysql常用命令(3)
一.启动与关闭 1.1 Linux下启动mysql 的命令: a. rpm包安装:service mysqld start b. 源码包安装:/usr/local/mysql/bin/mysqld_s ...
- Linux C Programing - Arguments(2)
#include <iostream> #include <stdlib.h> #include <stdio.h> //printf #include <u ...
- cocos2d-x的lua脚本加载CocostudioUI两种方式
前言 当前版本使用的是quick cocos2dx lua 3.3.UI使用cocostudio编辑器1.6.0.我们在程序里面可以使用两种方式进行解析UI.开始的时候用的是quick的方法, 结果遇 ...
- tas5721 驱动
Submitter Mark Brown Date March 31, 2016, 5:33 p.m. Message ID <E1algTO-0004Cm-Ga@finisterre> ...
- 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 ...