字符集转换(GBK->Unicode->Utf-8) - Gordon.Ma

时间:2024-02-23 13:58:55

字符集转换(GBK->Unicode->Utf-8)

  接触到汉字处理的同学一定都遇到过关于字符集的问题,通常因为前端和后端约定不一致导致显示或操作异常。
  本文整理了GBK、Unicode、UTF-8等编码方式的基本概念和区别,并提供了这几种编码方式的相互转换代码。

字符集

  • GBK
  早期的计算机使用7位的ASCII编码,为了处理汉字,于是有了用于简体中文的GB2312和用于繁体中文的big5。
 
  GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
 
  GBK(1995年)即汉字内码扩展规范,英文全称Chinese Internal Code Specification。GBK编码标准兼容GB2312,仍采用双字节表示,总体编码范围为8140-FEFE,首字节在81-FE 之间,尾字节在40-FE 之间,剔除 xx7F一条线。总计23940个码位,共收入21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个,简、繁体字融于一库。
 
  GB18030(2000年)是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、*文等主要的少数民族文字。GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK(中日韩)扩展A的6582个汉字。
  • Unicode
  Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。
 
  Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Universal Character Set"的缩写。UCS-2用两个字节编码,UCS-4用4个字节编码。
  • UTF-8
  UCS只是规定如何编码,并没有规定如何传输、保存这个编码。事实证明,对可以用ASCII表示的字符使用UNICODE并不高效,特别是在网络传输过程中。
 
  于是便有了UTF,即Unicode Transformation Format,意为Unicode传输格式。
 
  UTF-8就是以8位为单元对UCS进行编码,从UCS-2到UTF-8的编码方式如下:
UCS-2编码          UTF-8字节流(二进制)
0x0000 - 0x007F 0xxxxxxx
0x0080 - 0x07FF 110xxxxx 10xxxxxx
0x0800 - 0xFFFF 1110xxxx 10xxxxxx 10xxxxxx

  例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,要用3字节表示:1110xxxx 10xxxxxx 10xxxxxx。
 
  6C49的二进制是:0110 110001 001001,用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
 
  UTF-16以16位为单元对UCS进行编码。就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输。
  • BOM
  UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,如何判断呢?
 
  BOM —— Byte Order Mark,中文名译作“字节顺序标记”。
 
  在UCS编码中有一个叫做"Zero Width No-Break Space",中文译名作“零宽无间断间隔”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS 规范建议我们在传输字节流前,先传输字符 "Zero Width No-Break Space"。这样如果接收者收到FEFF,就表明这个字节流是 Big-Endian 的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符"Zero Width No-Break Space"(“零宽无间断间隔”)又被称作 BOM。
 
  UTF-8不需要BOM来表明字节顺序,但可以用 BOM 来表明编码方式。字符"Zero Width No-Break Space"的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。Windows就是使用BOM来标记文本文件的编码方式的。

编码转换

View Code

 1 unsigned Ucs2ToUtf8(const wchar_t* ucs2, char* utf8, unsigned utf8Length)
2 {
5 if (utf8Length == 0)
6 {
7 unsigned len = 0;
8 unsigned i = 0;
9 while (ucs2[i])
10 {
11 unsigned uch = ucs2[i++];
12 if (uch < 0x80)
13 len++;
14 else if (uch < 0x800)
15 len += 2;
16 else
17 len +=3;
18 }
19 return len + 1;
20 }
21
22 unsigned i = 0;
23 unsigned k = 0;
24 while (ucs2[i] && k < utf8Length)
25 {
26 unsigned int uch = ucs2[i++];
27 if (uch < 0x80)
28 {
29 utf8[k++] = static_cast<char>(uch);
30 }
31 else if (uch < 0x800)
32 {
33 utf8[k++] = static_cast<char>(0xC0 | (uch >> 6));
34 utf8[k++] = static_cast<char>(0x80 | (uch & 0x3f));
35 }
36 else
37 {
38 utf8[k++] = static_cast<char>(0xE0 | (uch >> 12));
39 utf8[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
40 utf8[k++] = static_cast<char>(0x80 | (uch & 0x3f));
41 }
42 }
43 if (k < utf8Length)
44 utf8[k++] = \'\0\';
45 return k;
46 }
47
48 unsigned Utf8ToUcs2(const char* utf8, wchar_t* ucs2, unsigned ucs2Length)
49 {
50 if (ucs2Length == 0)
53 {
54 unsigned len = 0;
55 unsigned i = 0;
56 while (utf8[i])
57 {
58 unsigned char ch = static_cast<unsigned char>(utf8[i++]);
59 if ((ch < 0x80) || (ch > (0x80 + 0x40)))
60 len++;
61 }
62 return len + 1;
63 }
64
65 unsigned i=0;
66 unsigned ui=0;
67 const unsigned char* us = reinterpret_cast<const unsigned char*>(utf8);
68 while (utf8[i] && ui < ucs2Length)
69 {
70 unsigned char ch = us[i++];
71 if (ch < 0x80)
72 {
73 ucs2[ui] = ch;
74 }
75 else if (ch < 0x80 + 0x40 + 0x20)
76 {
77 ucs2[ui] = static_cast<wchar_t>((ch & 0x1F) << 6);
78 ch = us[i++];
79 ucs2[ui] = static_cast<wchar_t>(ucs2[ui] + (ch & 0x7F));
80 }
81 else
82 {
83 ucs2[ui] = static_cast<wchar_t>((ch & 0xF) << 12);
84 ch = us[i++];
85 ucs2[ui] = static_cast<wchar_t>(ucs2[ui] + ((ch & 0x7F) << 6));
86 ch = us[i++];
87 ucs2[ui] = static_cast<wchar_t>(ucs2[ui] + (ch & 0x7F));
88 }
89 ui++;
90 }
91 if (ui < ucs2Length)
92 ucs2[ui++] = L\'\0\';
93 return ui;
94 }