字符集发展历程:
- ASCII。在所有字符集中,最知名的可能要数被称为ASCII的7位字符集了。它是美国标准信息交换代码(American Standard Code for Information Interchange)的缩写, 为美国英语通信所设计。它由128个字符组成,包括大小写字母、数字0-9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。
- Unicode。ASCII是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的亚洲文字时就会出现问题。因此,创建出了一些包括255个字符的由ASCII扩展的字符集。其中有一种通常被称为IBM字符集,它把值为128-255之间的字符用于画图和画线,以及一些特殊的欧洲字符。另一种8位字符集是ISO 8859-1Latin 1,也简称为ISOLatin-1。它把位于128-255之间的字符用于拉丁字母表中特殊语言字符的编码,也因此而得名。欧洲语言不是地球上的唯一语言,因此亚洲和非洲语言并不能被8位字符集所支持。仅汉语字母表(或pictograms)就有80000以上个字符。但是把汉语、日语和越南语的一些相似的字符结合起来,在不同的语言里,使不同的字符代表不同的字,这样只用2个字节就可以编码地球上几乎所有地区的文字。因此,创建了UNICODE编码。它通过增加一个高字节对ISO Latin-1字符集进行扩展,当这些高字节位为0时,低字节就是ISO Latin-1字符。UNICODE支持欧洲、非洲、中东、亚洲(包括统一标准的东亚象形汉字和韩国表音文字)。但是,UNICODE并没有提供对诸如Braille,Cherokee, Ethiopic, Khmer, Mongolian, Hmong, Tai Lu, Tai Mau文字的支持。同时它也不支持如Ahom, Akkadian, Aramaic, Babylonian Cuneiform, Balti, Brahmi, Etruscan, Hittite, Javanese, Numidian, Old Persian Cuneiform, Syrian之类的古老文字。
- UTF。对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODE比ASCII占用大一倍的空间,而对ASCII来说高字节的0对他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即UTF(Unicode Transformation Format)。常见的UTF格式有:UTF-7, UTF-7.5, UTF-8,UTF-16, 以及 UTF-32。
编码:
如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节。而如果UNICODE字符由4个字节表示,则编码成UTF-8可能需要6个字节。用4个或6个字节去编码一个UNICODE字符可能太多了,但很少会遇到那样的UNICODE字符。
UTF-8编码规则:如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8转换表表示如下:Unicode/UCS-4
Unicode/UCS-4
bit数 UTF-8 byte数 备注 0000 ~007F 0~7 0XXX XXXX 1 0080 ~07FF 8~11 110X XXXX10XX XXXX 2 0800 ~FFFF 12~16 1110XXXX10XX XXXX10XX XXXX 3 基本定义范围:0~FFFF 1 0000 ~1F FFFF 17~21 1111 0XXX10XX XXXX10XX XXXX10XX XXXX 4 Unicode6.1定义范围:0~10 FFFF 20 0000 ~3FF FFFF 22~26 1111 10XX10XX XXXX10XX XXXX10XX XXXX10XX XXXX 5 说明:此非unicode编码范围,属于UCS-4 编码早期的规范UTF-8可以到达6字节序列,可以覆盖到31位元(通用字符集原来的极限)。尽管如此,2003年11月UTF-8 被 RFC 3629 重新规范,只能使用原来Unicode定义的区域, U+0000到U+10FFFF。根据规范,这些字节值将无法出现在合法 UTF-8序列中 400 0000 ~7FFF FFFF 27~31 1111 110X10XX XXXX10XX XXXX10XX XXXX10XX XXXX10XX XXXX 6
VS2010的字符集:
代码中直接定义的字符串是GB2312字符集,字符串前面加L表示该字符串是Unicode字符串,_T是一个宏,如果项目使用了Unicode字符集(定义了UNICODE宏),则自动在字符串前面加上L
char *p_string_E="A"; //GB2312编码 char *p_string_C="码"; //GB2312编码 wchar_t *p_string_C_L=L"码"; //Unicode编码
Linux的字符集
Linux定义的字符串,使用gnu编译时字符集由文件的编码方式决定。
字符集转换:
GB2312转Unicode:使用windows API::MultiByteToWideChar
bool GB2312ToUnicode(char *input_string, wchar_t *out_buffer, int out_buffer_len) { int nwLen = ::MultiByteToWideChar(CP_ACP, 0, input_string, -1, NULL, 0); if(nwLen>=out_buffer_len) { return false; } ZeroMemory(out_buffer, nwLen * 2 + 2); ::MultiByteToWideChar(CP_ACP, 0, input_string, strlen(input_string), out_buffer, nwLen); return true; }
GB2312转UTF-8:使用windows API::MultiByteToWideChar和WideCharToMultiByte,先转成Unicode,再从Unicode转成UTF8
bool GB2312ToUTF8(char *input_string, char *out_buffer, int out_buffer_len) { int nwLen = ::MultiByteToWideChar(CP_ACP, 0, input_string, -1, NULL, 0); wchar_t * pwBuf = new wchar_t[nwLen + 1];//+1的作用是保证字符串有结束符 ZeroMemory(pwBuf, nwLen * 2 + 2); ::MultiByteToWideChar(CP_ACP, 0, input_string, strlen(input_string), pwBuf, nwLen); int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL); if(nLen>=out_buffer_len) { delete []pwBuf; return false; } ZeroMemory(out_buffer, nLen + 1); ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, out_buffer, nLen, NULL, NULL); delete []pwBuf; return true; }
测试字符集编码:
#include "stdafx.h" #include "string.h" #include <stdio.h> #include <windows.h> bool GB2312ToUTF8(char *input_string, char *out_buffer, int out_buffer_len) { int nwLen = ::MultiByteToWideChar(CP_ACP, 0, input_string, -1, NULL, 0); wchar_t * pwBuf = new wchar_t[nwLen + 1];//+1的作用是保证字符串有结束符 ZeroMemory(pwBuf, nwLen * 2 + 2); ::MultiByteToWideChar(CP_ACP, 0, input_string, strlen(input_string), pwBuf, nwLen); int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL); if(nLen>=out_buffer_len) { delete []pwBuf; return false; } ZeroMemory(out_buffer, nLen + 1); ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, out_buffer, nLen, NULL, NULL); delete []pwBuf; return true; } bool GB2312ToUnicode(char *input_string, wchar_t *out_buffer, int out_buffer_len) { int nwLen = ::MultiByteToWideChar(CP_ACP, 0, input_string, -1, NULL, 0); if(nwLen>=out_buffer_len) { return false; } ZeroMemory(out_buffer, nwLen * 2 + 2); ::MultiByteToWideChar(CP_ACP, 0, input_string, strlen(input_string), out_buffer, nwLen); return true; } void PrintDataBuffer(char *buffer_name, void *p_buffer, int buffer_len) { printf("%s[%d]:", buffer_name, buffer_len); unsigned char *p_temp=(unsigned char *)p_buffer; for(int iloop=0;iloop<buffer_len;iloop++) { printf("%02X ", p_temp[iloop]); } printf("\n"); } int _tmain(int argc, _TCHAR* argv[]) { char *p_string_E="A"; //GB2312编码 char *p_string_C="码"; //GB2312编码 wchar_t *p_string_C_L=L"码"; //Unicode编码 PrintDataBuffer("原始编码\"A\"", p_string_E, strlen(p_string_E)); PrintDataBuffer("原始编码\"码\"", p_string_C, strlen(p_string_C)); PrintDataBuffer("原始编码L\"码\"", p_string_C_L, sizeof(wchar_t)*wcslen(p_string_C_L)); wchar_t wchar_E_buffer[32]; wchar_t wchar_C_buffer[32]; GB2312ToUnicode(p_string_E, wchar_E_buffer, 32); GB2312ToUnicode(p_string_C, wchar_C_buffer, 32); PrintDataBuffer("Unicode编码\"A\"", wchar_E_buffer, sizeof(wchar_t)*wcslen(wchar_E_buffer)); PrintDataBuffer("Unicode编码\"码\"", wchar_C_buffer, sizeof(wchar_t)*wcslen(wchar_C_buffer)); char utf8_E_buffer[32]; char utf8_C_buffer[32]; GB2312ToUTF8(p_string_E, utf8_E_buffer, 32); GB2312ToUTF8(p_string_C, utf8_C_buffer, 32); PrintDataBuffer("UTF-8编码\"A\"", utf8_E_buffer, strlen(utf8_E_buffer)); PrintDataBuffer("UTF-8编码\"码\"", utf8_C_buffer, strlen(utf8_C_buffer)); getchar(); return 0; }