字符编码、字符存储、字符转换及工程中字符的使用

本文详细介绍了字符编码的历史、常见的字符编码问题、Unicode、UTF8、UTF16、大端小端、BOM以及工程中字符的使用,包括Windows C++环境下字符的存储、转换和文件编码。通过对字符理论的理解,帮助开发者解决编码匹配、乱码等问题。
摘要由CSDN通过智能技术生成

字符编码、字符存储、字符转换及工程中字符的使用

 

版本控制

版本

时间(北京时间)

作者

备注

V1.0

2016-05-13

施小丰

创建本文、第七章工程总结尚未完成

 

 

 

 

 

 

一、          前言

1.        目的

本文主要用于整理字符相关知识,包括字符编码、字符存储、行业标准、文件读写、工程注意事项等涉及字符相关的内容,

从而在实际工程中更好地设计和使用字符、更快地解决字符问题。

 

2.        适用范围

本文标题是“Windows C++字符编码、存储、转换大全”,

但“第三段.行业标准“属于概念总结,不涉及具体的编程语言和平台,是通用的知识,读者无需编程基础,但需要一定的计算机基础。

第四段到第七段仅适用于Windows C++ VC环境,需要读者具备一定的C++开发基础。

 

3.        开发环境及辅助工具

操作系统:  Windows7 64bit中文版

IDE:       VS2008+Sp1

WinHex,   下载地址http://www.winhex.com/winhex/index-m.html

Notepad++:下载地址https://notepad-plus-plus.org/

 

4.        声明

本文由施小丰发表于http://www.smallgui.com/,任何人以任何形式转载时,请确保本文完整,包括本节权利声明。

 

二、          常见问题

在使用Windows、C++、VC编程时,经常遇到以下个问题

 

1.        函数调用时参数字符编码不匹配

比如提示cannot convertparameter 3 from 'LPCWSTR' to 'const char *'

 

2.        存储或解析字符后是乱码

比如程序存储数据至文本文件后,使用Notepad打开发现是乱码。

 

3.        网络请求时由于字符问题导致乱码

比如发送http请求时,由于编码格式不对,对方服务器无法正确解析数据导致没有服务器正确执行预期操作

 

三、          字符理论

面对字符编码,我们不禁要问,怎么会产生字符编码问题,为什么不能统一字符编码?那就得先看下字符编码的历史了。

 

1.        标准ASCII码

计算机内的信息本质上都是二进制信息,即只有0和1两种状态,则此时字符本身,比如'A'需要用多个二进制位表示。

所以为统一各种字符的二进制值,美国国家标准委员会(American National Standard Institute,ANSI)于上个世纪60年代制定了叫做ASCII码(AmericanStandard Code for Information Interchange,美国标准信息交换码。)的字符编码。

标准ASCII码表使用8个二进制,最高位统一为0,所以实际使用7个低二进制位,从而规定了128个字符(2的7次方)的编码,见本文附件1.ASCII码表。

(这里其实来到了计算机行业一个恒久不变的坑:“够了”,比如这里的ASCII码表,比如当时的IP地址,比如比尔盖茨先生的“640K ought to be enough for anyone”,比如千年虫问题。。。)

 

2.        非标准ASCII码

纯英文使用ASCII码或许够了,但到非英语国家时,显然128个字符不够用,于是很多国家都充分利用起最高位来引入新的符号,

但每个国家对于扩展位并没有统一,于是出现”130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。”。

到目前为止ASCII已经不够用了,必须设计新的编码方式,但新编码方式至少需要解决六个问题

(1)0-127各国都一样,都是标准ASCII码,新编码方式需要兼容

(2)如何统一现有的所有字符

(3)如何在兼容原有字符的基础上,能在未来扩展新字符

(4)新编码是否能够不显著增加小子集字符国家的复杂度,比如对纯英语国家,128种字符够用,完全用全新的多个字节的编码方式,太浪费了。

(5)如何知道一个文件的编码格式和存储方式(大端小端),从而使用正确解析文件内容

(6)如何确保在网络传输过程中双方的编码和解码一致

 

3.        Unicode、UCS2、UCS4

为了解决上述六个问题,有两个国际组织试图设计Unicode,分别是ISO(国际标准化组织)和Unicode.org(软件制造商协会),ISO开发ISO 10646项目,Unicode开发Unicode项目。

从1999年的Unicode3.0开始,Unicode项目和ISO10646项目的字库和字符已经一样,所以我们可以认为他们是一样的。

Unicode的目的是给符号进行编码(注意不是存储,也不是传输),目前常用的实现是UCS2,使用两个字节(16位)来表示一个字符,如汉字“施”的Unicode编码为\u65bd(可用在线工具http://tool.chinaz.com/tools/unicode.aspx转换)

,其中\u表示这是一个unicode编码(实际可忽略这个\u,仅仅为了区分而标记),其编码为十六进制的65bd。

了解了其形式,我们看看针对上面提出的六个问题,Unicode是如何解决的:

(1)标准ASCII码在Unicode编码中没变,比如字母A在ASCII中为0x41,在Unicode中的UCS2(实际上我们一般直接称UCS2为Unicode)实现中为\u0041,只是高位被用0填充了,所以第一个问题解决了

(2)Unicode为每个字符定义了一个唯一的编号,且字符对应的编码确定好以后,不再更改(至少低位不更改,高位可能由于后续扩展会统一填充),第二个问题也解决了

(3)ASCII码因为只有8个二进制位,所以不够用,而Unicode标准本身并没有规定具体多少个字节,

但真正实现Unicode则必须考虑这个问题,现在实际用的最多的是用两个字节(即16位,共有65536钟表示)表示一个符号,这种实现方式称做UCS2。UCS2实际上是Unicode的一个子集,

也有用四个字节表示一个字符的称作UCS4,所以未来如果忽然增加了很多新字符,则可以设计UCS8、UCS16等实现,且低位兼容UCS2和UCS4即可,第三个问题也解决了

(4)(5)(6)三个问题涉及到编码的具体存储和协商方式,详见第四小节4.UTF8、UTF16, 第五小节5.大端小端, 第六小节6.codepage和charset

 

4.        UTF8、UTF16

UTF8或者UTF16的出现,本质上是解决上面6个问题中的第4个,即如果所有的字符都用相同字节的编码来表示,那么对于小字符集国家而言,这个存储太浪费空间了(可能当年的存储成本很高导致)。

这里以UTF-8为例,UTF-8是对Unicode编码格式(一般平时所说的Unicode等同于UCS2)的一种存储实现。UTF-8和UCS2的转换关系如下所示

UCS2编码(16进制)      UTF8字节流(二进制)      备注

0000-007F                 0xxxxxxx                   其实就是标准ASCII码

0080-07FF                 110xxxxx 10xxxxxx

0800-FFFF                 1110xxxx 10xxxxxx 10xxxxxx

以汉字“施”为例,其unicode编码为65bd,也即Unicode二进制代码为

110010110111101

则转换成UTF8编码时,使用上述二进制代码从右往左依次填充1110xxxx 10xxxxxx 10xxxxxx中的x,左边不足部分使用0填充,得到其UTF8编码二进制值为

11100110 10010110  10111101

转换成16进制,即为E6 96 BD,我们在记事本中写入施,然后另存为UTF8,格式,再使用WinHex观察,前三个字节EF BB BF属于BOM,后面三个字节就是汉字“施”的UTF8编码,好了,新的问题又出现了,BOM是什么?

 

5.        大端、小端、BOM、零宽度非换行空格

汉字“施”的Unicode编码(再次提醒,其实一般所说的Unicode编码就是指UCS2编码)是65bd,占用两个字节,

于是就存在两种存储方式,

大端:65在高位的存储方式就是大端

小端:65在低位的存储方式叫做小端

为了区分当前存储或传输方式到底是大端还是小端,Unicode

BOM:字节顺序标记,FF FE

零宽度非换行空格:我们可以理解成BOM的一个组成部分

 

通过在windows中的Notepad++中写入字符保存为不同的格式,然后用WinHex打开可以得到不同编码的字节顺序标记(本表仅在Windows平台下测试过)

 

编码

BOM(十六进制)

备注

ASCII

相当于txt另存为ASCII

UTF-8

EF BB BF

相当于txt另存为utf8

UTF-8无BOM

相当于UTF-8去掉文件头BOM

小端

FF FE

相当于txt另存为Unicode

大端

FE FF

相当于txt另存为Unicode big endian

 

上节中提到的我们存储的汉字“施”就是使用UTF8编码,其中前面三个字节就是BOM。

但是这个BOM其实是Windows的“特产”,所以存储UTF8时,最好不要带BOM。网上这篇文章http://www.cnblogs.com/findumars/p/3620078.html对于UTF8应不应该带BOM讲的比较清楚。结论是UTF-8尽量不要带BOM。

 

那么新问题又来了,假如我们在文件中只是存储字符的UTF8码,而没有BOM信息的话,我们在解析文件时,如何确定编码方式,尤其在网络上传输信息,比如html的时候,浏览器如何确定编码方式?

 

6.        文件编码和charset

(1)文件编码

第一种情况是纯文本文件,且没有BOM信息,这种情况下从纯软件的角度来讲其实只能猜测尝试编码方式;可以按照http://blog.csdn.net/turingo/article/details/8136644这篇博文实现。

第二种情况是纯文本文件,有BOM信息,这种情况下从通过BOM信息判断文件的编码格式。

第三种情况是文件本身已经描述了其编码方式,比如标准xml在声明中需要表明其本身的编码方式,一般形式如下:<?xml version="1.0" encoding="utf-8"?>,其中encoding表明文件本身是用utf-8编码的。

 

(2)网络传输

第一种情况是自定义的socket的话,需要双方自己协商确定编码格式。

另外一种是标准协议或语言,比如html。Html标记语言有一个属性charset,这个属性在html中一般是head节点的第一个子节点,这样浏览器解析html时,会先用默认的编码格式读取一部分html数据(比如GB2312),如果读取到当前html的charset后,与当前默认的编码格式不同,则浏览器使用charset中指定的编码重新读取html并显示。下面我们来验证一下。

使用Chrome浏览器打开百度首页后,右击查看网页源代码后获取的百度首页的html内容,

 

我们右击另存到本地计算机上后,使用Notepad++打开该文件,然后再使用Chrome打开,字符显示正常(样式会由于css样式表确实导致走样)


然后将其中的utf-8修改为GB2312


再刷新刚打开的页面,发现字符显示已经是乱码


所以对于网页或者传输而言,你要知道你传输的字符或者文件的编码格式,然后由你自己告诉对方你的编码格式,如果你告诉别人的编码方式和实际文件的编码格式不一致,那么结果很可能是两个字:乱码。

 

 

四、          常用字符编码标准

 

ID

标准名称

作用

备注

1

标准ASCII

特指0-127共128个字符

只适用于纯英语环境

2

Unicode

为世界上每个字符分配一个编码

只规定了编号,未规定存储和传输方式

3

UCS2

Unicode编码的一种实现

使用两个字节表示一个字符

实际是Unicode的一个子集

通常所说的Unicode就指UCS2

4

UTF8

Unicode编码存储的一种实现

字节可变

 

5

GB2312

 

又称GB2312-80

6

GBK

 

 

 

 

五、          字符定义总结

在C++体系中,字符类型本质上只有char和wchar_t两种,其他的要么只是封装,比如string封装了char数组,wstring封装了wchar_t数组,要么是为了兼容不同的语言或组件,比如CString是MFC框架下的字符串,_bstr_t是COM组件中的字符串。下表列出了常用的字符类型。

 

分类

ID

名称

备注

标准C++

 

char

单字节

 

wchar_t

宽字节,在string.h中定义

实际是unsigned short

需使用wcs前缀的函数处理wchar_t

wchar_t*  myStr=L"测试";

 

string

 

 

wstring

 

VC

 

CHAR

等同于char

 

WCHAR

等同于wchar_t

 

TCHAR

根据_UNICODE宏,确定当前表示的是ANSI还是Unicode

所以应配合_T使用初始化字符串

且应使用以_tcs为前缀的函数

 

_T  TEXT  _TEXT

三个作用一样,

根据环境决定是ASCII还是Unicode

 

L

将字符串变成Unicode

MFC

 

CString

MFC字符串

COM相关

 

OLECHAR

不同环境下自动为

WCHAR或CHAR

 

BSTR

是一个有长度有前缀和null结束符的OLECHAR数组

是COM中默认的字符串格式

 

_bstr_t

是对BSTR的封装的类

实际是一个智能指针

为实现和LPCSTR和BSTR才有的

 

CComBSTR

ATL中的类

是对BSTR类型的分装

 

VARIANT

可变类型

 

_variant_t

是对VARIAN的封装,类似于_bstr_t对于BSTR的封装

 

COleVariant

是对VARIANT的封装

 

其它类型见WinNT.h头文件:

typedef WCHAR *PWCHAR,*LPWCH, *PWCH;
typedef CONST WCHAR *LPCWCH, *PCWCH;

typedef _Null_terminated_WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef _Null_terminated_ PWSTR *PZPWSTR;
typedef _Null_terminated_ CON

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值