关于字符集,编码格式,大小端的简单总结

只要你和计算机打交道,这些问题能够说是每天会遇到,可是不少人是似懂非懂, 能真正彻底理解的人却很少, 下面是我的的一些理解,有错欢迎指正. html


最先的计算机只支持ASCII码, 具体来讲就是用1个字节(最高位为0, 没有用)表示0到127,总共128个字符, 这样就能够彻底知足英语应用的要求了。

后来扩展到欧洲语系,理论上一个字节能够表示256个字符, 欧洲人就把剩余的128个字符(最高位为1)按照本身语言(法语,德语...)的要求扩充应用了起来, 好像也能知足须要。

而后又到了亚洲国家,好比中国,中文汉字有十多万,这剩余的128个字符根本不够我用啊, 怎么办? 因而就有了两个字节的编码,如中文的GBK, GB2312, BIG5等,固然日语,韩语等其余亚洲国家也有本身编码方式。

这就是所谓的多字节编码(MBCS)方式, Win95/98时代只支持这种方式, 那时候处理字符串很是痛苦, 由于它里面有些字符是一个字节表示的,也有一些是多个字节表示的, 好比字符串"你好abc", 里面明明是5个字符,strlen返回长度倒是7, 你要正确识别字符个数,可使用相似_mbslen的API, 可是实际上该API内部会绑定当前的字符集, 否则神仙也识别不了。

要统一解决上面的问题, 须要有一个世界通用的统一编码格式, 那就是UNICODE。
 UNICODE我的感受分广义和狭义, 广义的UNICODE包括UTF8, UCS2, UCS4, 而狭义的UNICODE(主要是Windows平台)就是指UCS2。
先说UCS2, Windows平台上常说的UNICODE实际上就是指UCS2, 简单来讲就是统一用2个字节的编码,表示实际上全部语言的经常使用字符。
再说UTF8, 有了上面的UCS2,为甚么还有要UTF8? UCS2把任何字符全都编码成2个字节(包括咱们经常使用的英文字符), 这样极大地增长了网络传输和数据存储的开销,因而就有了UTF8。UTF8对英文字符仍是1个字节存储,只对其余语言字符用多个字节存储(2-6个字节)。UTF8和UNICODE能够彻底对应的相互转换, 具体能够参考这里
为何还要有 UCS4? UCS2用2个字节,最多也只能表示0xFFFF+1 = 65536个字符, 可是咱们仅汉字就有十多万,因此UCS2/UTF8收录的也只是咱们一些经常使用的汉字, 因此咱们须要UCS4, 用4个字节表示一个字符,能够表示0xFFFFFFFF+1=4294967296个字符, 它才是咱们之后的终极解决方案。

在Windows上不一样编码方式的相互转换能够经过WideCharToMultiByteMutiByteToWideChar进行, 它里面WideChar就是指UCS2, 能够看到它这里把UTF8也命名成MultiByte, 是否是有点误导...

下面再谈小大小端(little-endian, big-endian).

计算机是以字节为寻址单位的,这就涉及到字(2个字节), 双字(4个字节)及其余多字节单位 在计算机内如何排布的问题, 这里无非就是2种:低字节在低地址的little-endian和高字节在低地址的big-endian.
如何区分当前系统是哪一种类型的大小端? 曾经看到有经验的程序员也以当前的操做系统类型来判断, 实际上系统的大小端和你的CPU架构体系相关联, 好比说X86是小端, PowPC是大端,ARM则是可控制(默认也是小端)。
要判断当前环境是大小端实际上很简单: bool IsLittleEndian()  {  int i=1;  return (*(char *)&i == 1); }
曾经看到公司跨平台的代码没有经过大小端转换,直接经过memcpy某个表示长度的int在客户端之间传送,却没有发生问题, 感受很奇怪, 最后发现原来当前公司的全部客户端(Win, Mac, ios, Android,BlackBerry,Linux)全都是小端。感受如今大端的应用主要是网络字节序, Java内部全都是大端。
上面的UCS2和UCS4由于都是用多字节表示一个字符, 因此实际上都有大小端的问题,好比分别对应UCS2-LE和UCS2-BE,Windows上的UNICODE其实是UCS2-LE, UTF8由于是字节流,因此没有大小端的问题。

下面再说一下BOM (Byte Order Mark), 上面说了各类编码方式以及大小端的问题, 那么咱们怎么知道某个文本或者数据流是何种编码方式? 
通常来讲有3种方法:一种是分本显示指定, 好比web里html头通常会有这么一段"content="text/html;charset=utf-8"; 要不就是你们默认约定,好比自定义的网络数据流内的字符串通常都会用UTF8编码; 还有一种就是用BOM,经过在文件头里填入BOM规定的字节,从而区分文件是何种编码类型: 

UTF-8 0xEF 0xBB 0xBF
UTF-16 BE 0xFE 0xFF
UTF-16 LE 0xFF 0xFE
UTF-32 BE 0x00 0x00 0xFE 0xFF
UTF-32 LE 0xFF 0xFE 0x00 0x00

有兴趣的同窗能够用notepad保存,试下各类效果, 而后用UltraEdit的16进制方式查看验证。

最后讨论下C++编程中常见的关于字符编码方式相关的问题。

在C++编程中, 咱们常打交道的无非是编辑器和编译器, 对编辑器起来讲,咱们常遇到就是乱码问题, 好比中文注释显示或是保存不了等, 解决办法就是把你的文件保存成Unicode(UTF8)。
对于编译器来讲, 编码方式取决于它对C++标准的支持程度, 好比C++ 11之前,字符串咱们只能指定成2种:一种是MBCS,如char* p="abc哈哈"; 还有一种是UCS2, 好比wchar_t*p = L"abc哈哈", 这样编译器就知道你要表示的字符串类型。C++11以后,标准增长了UTF8和UCS4的支持, 好比char* p=u8"abc哈哈"表示UTF8,wchar_t * p=u"abc哈哈"表示UCS2(实际上和L"xxxx"同样),  char32_t* p=U"abc哈哈"表示UCS4。这里要区分编译期和运行期, 尽管C++11以前咱们无法告诉编译器咱们这个常量串是UTF8格式的,可是程序期咱们仍是可使用全部的编码式 (MBCS/UTF8/UCS2/UCS4), 由于这些最终在内存里都是二进制流。
另外C++11还增长了UTF8, UCS2, UCS4相互转码的支持:
std::codecvt_utf8 封装了UTF8相关的编码转换
std::codecvt_utf16 封装了UCS2相关的编码转换
std::codecvt_utf8_utf16 封装了UTF8与UCS2的编码转换

对于C++跨平台开发, 咱们常常遇到的就是默认用那种编码方式的问题,咱们会发现Windows 的UCS2解决方案对其余平台来讲是个异类, 通常来讲有2种解决方法:
一种是统一用UTF8 , 可是这样对Windows来讲有点麻烦, 由于Windows的API都是UCS2的,因此这种方式意味着任何字符串在传给Windows API 以前都要从UTF8转成UCS2; 还有一种就是用#define宏了, Windows上将字符串相关宏全都定义成UCS2, 其余平台则全都定义成UTF8, 该方式要求就你在写代码时头脑要比较清醒,由于一样的代码在不一样平台上的编码格式是不同的。

一直很好奇,谁知道Windows为何不用UTF8,非要搞得和其余平台不同?
相关文章
相关标签/搜索