详解字符编码
有问题欢迎发 Email: qiuduming@163com
最近需要对 Linux 与 Windows 平台下的字符传输出现乱码,对字符编码作了深究。参考
了网上的 UTF‐8/UTF‐16 转换的资料,只有 0x10000 以下的 Unicode 编码进行了转换;对其代
码进行了修改和补充,可以实现所有的 UTF‐8/UTF‐16 的转换,分享给大家。(看代码前提是
需了解 Unicode 的编码知识)。
介绍:
一、Ascii 编码(摘自维基百科)
ASCII(美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用
于显示现代英语,而其扩展版本 EASCII 则可以部分支持其他西欧语言,并等同于
国际标准 ISO/IEC 646。由于万维网使得 ASCII 广为通用,直到 2007 年 12 月,逐
渐被 Unicode 取代[2]。
ASCII 第一次以规范标准的型态发表是在 1967 年,最后一次更新则是在 1986 年,
至今为止共定义了 128 个字符;其中 33 个字符无法显示(一些终端提供了扩展,
使得这些字符可显示为诸如笑脸、扑克牌花式等 8-bit 符号),且这 33 个字符多
数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在
33 个字符之外的是 95 个可显示的字符,包含用键盘敲下空白键所产生的空白字符
也算 1 个可显示字符(显示为空白)。
二、ANSI 与 GBK 编码(摘自百度百科)
ANSI(American National Standards Institute),中文:美国国家标准学
会。
为使计算机支持更多语言,通常使用 0x80~0xFF 范围的多个字节来表示 1 个
字符。比如:汉字 '中' 在简体中文 Windows 操作系统中,使用 [0xD6,0xD0] 这
两个字节存储。对于 ANSI 编码而言,0x00~0x7F 之间的字符,依旧是 1 个字节代
表 1 个字符。这一点是 ANSI 编码与 UTF-16 编码之间最大也最明显的区别。比如“A
君是第 131 号”,在 ANSI 编码中,占用 12 个字节,而在 UTF-16 编码中,占用 16
个字节。因为 A 和 1、3、1 这 4 个字符,在 ANSI 编码中只各占 1 个字节,而在 UTF-16
编码中,是需要各占 2 个字节的。
不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、
Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉
字延伸编码方式,称为 ANSI 编码。在简体中文 Windows 操作系统中,ANSI 编码
代表 GBK 编码;在日文 Windows 操作系统中,ANSI 编码代表 Shift_JIS 编码。 不
同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,
存储在同一段 ANSI 编码的文本中。 当然对于 ANSI 编码而言,0x00~0x7F 之间的
字符,依旧是 1 个字节代表 1 个字符。这一点是 ASNI 编码与 Unicode 编码之间最
大也最明显的区别。
三、Unicode 编码(摘自维基百科)
Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的
一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用
更为简单的方式来呈现和处理文字。
目前 Unicode 编码的范围是 0x0——0x10FFFF。Unicode 流行的编码方式有的
UCS‐2、UCS4 和 UTF‐8、UTF‐16、UTF‐32。
注:至于 UTF‐8、UTF‐16、UTF‐32 的编码方式,大家可以在维基百科上搜索,上面有详细的
介绍。
UTF‐8 和 UTF‐16 的转换代码:
#include
#include
#include
using namespace std;
typedef unsigned short utf16;
typedef unsigned char utf8;
void UTF8toUTF16(utf8* p8begin ,utf8* p8end,utf16* p16begin ,utf16* p16end)
{
utf8 * p8start = p8begin;
utf16* p16start = p16begin;
while (p8start<=p8end&&p16start<=p16end)
{
if (*p8start>=0&&*p8start<=0x7f)
{
}
else if (*p8start>=0xc0&&*p8start<=0xdf)
{
*p16start |= (*p8start++&0x1f)<<6;
//*p16start |= (*p8start++&0x3f);
*p16start |= (*p8start&0x3f);
if (p8startp8start++;
if (p8start
>4;
else if (*p8start>=0xe0&&*p8start<=0xef)
{
}
else if (*p8start>=0xf0&&*p8start<=0xf7&&p16start+1<=p16end)
{
*p16start |= (*p8start++&0x0f)<<12;
*p16start |= (*p8start++&0x3f)<<6;
//*p16start |= (*p8start++&0x3f);
*p16start |= (*p8start&0x3f);
}
//*p16start = 0;
}
void UTF16toUTF8(utf16* p16begin ,utf16* p16end ,utf8* p8begin ,utf8* p8end)
{
utf8* p8start = p8begin;
utf16* p16start = p16begin;
while (p16start<=p16end&&p8start<=p8end)
p8start++;
if (p8startbreak;
}
*p8start++ =(*p16start>>6)|0xc0;
//*p8start++ = ((*p16start)&0x3f)|0x80;
*p8start = ((*p16start)&0x3f)|0x80;
if (p8start
0x7f&&*p16start<=0x7ff&&p8start+1<=p8end)
{
{
else if(((0x7ff<*p16start&&*p16start<0xd800)|| \
(0xdfff<*p16start&&*p16start<0xffff))&&p8start+2<=p8end)
p8start+3<=p8end&&p16start+1<=p16end)
*p16start = (*p16start&0x3ff);
*p8start++ = (*p16start>>8)|0xf0;
*p8start++ = ((*p16start&0xff)>>2)|0x80;
*p8start = ((*p16start++&0x3)<<4)|0x80;
*p16start = (*p16start&0x3ff);
*p8start++ |= (*p16start>>6);
//*p8start++ = (*p16start&0x3f)|0x80;
*p8start = (*p16start&0x3f)|0x80;
*p8start++=(*p16start>>12)|0xe0;
*p8start++=((*p16start>>6)&0x3f)|0x80;
//*p8start++=(*p16start&0x3f)|0x80;
*p8start=(*p16start&0x3f)|0x80;
if (p8start