多字节编码与Unicode码

 

多字节字符与宽字节字符

1) char与wchar_t

我们知晓C++基本数据类型中意味着字符的有二种:char、wchar_t。 
char叫多字节字符,一个char占一个字节,之所以叫多字节字符是因为它象征一个时可能是一个字节也可能是五个字节。一个英文字符(如’s’)用一个char(一个字节)表示,一个普通话汉字(如’中’)用3个char(五个字节)表示,看下面的事例。

 1 void TestChar()
 2 {
 3     char ch1 = 's';             // 正确
 4     cout << "ch1:" << ch1 << endl;
 5     char ch2 = '中';             // 错误,一个char不能完整存放一个汉字信息
 6     cout << "ch2:" << ch2 << endl;
 7 
 8     char str[4] = "中";          //前三个字节存放汉字'中',最后一个字节存放字符串结束符\0
 9     cout << "str:" << str << endl;
10     //char str2[2] = "国";       // 错误:'str2' : array bounds overflow
11     //cout << str2 << endl;
12 }

结果如下:

ch1:s 
ch2: 
str:中

wchar_t被称为宽字符,一个wchar_t占2个字节。之所以叫宽字符是因为具有的字都要用五个字节(即一个wchar_t)来表示,不管是英文依然中文。看下面的例证:

 1 void TestWchar_t()
 2 {
 3     wcout.imbue(locale("chs"));     // 将wcout的本地化语言设置为中文
 4 
 5     wchar_t wch1 = L's';            // 正确
 6     wcout << "ch1:" << wch1 << endl;
 7     wchar_t wch2 = L'中';            // 正确,一个汉字用一个wchar_t表示
 8     wcout << "ch2:" << wch2 << endl;
 9 
10     wchar_t wstr[2] = L"中";         // 前两个字节(前一个wchar_t)存放汉字'中',最后两个字节(后一个wchar_t)存放字符串结束符\0
11     wcout << "str:" << wstr << endl;
12     wchar_t wstr2[3] = L"中国";
13     wcout << "str2:" << wstr2 << endl;
14 }

结果如下:

ch1:s 
ch2:中 
str:中 
str2:中国

说明: 
1.
用常量字符给wchar_t变量赋值时,前面要加L。如: wchar_t wch2 =
L’中’; 
2.
用常量字符串给wchar_t数组赋值时,前边要加L。如: wchar_t wstr2[3] =
L”中国”; 
3.
只要不加L,对于英文可以正常,但对于非英文(如普通话)会出错。

 

2)string与wstring

字符数组能够代表一个字符串,但它是一个定长的字符串,我们在动用从前必须领悟这些数组的长短。为便于字符串的操作,STL为我们定义好了字符串的类string和wstring。我们对string肯定不生疏,但wstring可能就用的少了。

string是日常的多字节版本,是依照char的,对char数组进行的一种包装。

wstring是Unicode版本,是基于wchar_t的,对wchar_t数组进行的一种包装。

string与wstring的有关转换

以下的多少个章程是跨平台的,可在Windows下接纳,也可在Linux下使用。

 1 #include <cstdlib>
 2 #include <string.h>
 3 #include <string>
 4 
 5 // wstring => string
 6 std::string WString2String(const std::wstring& ws)
 7 {
 8     std::string strLocale = setlocale(LC_ALL, "");
 9     const wchar_t* wchSrc = ws.c_str();
10     size_t nDestSize = wcstombs(NULL, wchSrc, 0) + 1;
11     char *chDest = new char[nDestSize];
12     memset(chDest,0,nDestSize);
13     wcstombs(chDest,wchSrc,nDestSize);
14     std::string strResult = chDest;
15     delete []chDest;
16     setlocale(LC_ALL, strLocale.c_str());
17     return strResult;
18 }
19 
20 // string => wstring
21 std::wstring String2WString(const std::string& s)
22 {
23     std::string strLocale = setlocale(LC_ALL, ""); 
24     const char* chSrc = s.c_str();
25     size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1;
26     wchar_t* wchDest = new wchar_t[nDestSize];
27     wmemset(wchDest, 0, nDestSize);
28     mbstowcs(wchDest,chSrc,nDestSize);
29     std::wstring wstrResult = wchDest;
30     delete []wchDest;
31     setlocale(LC_ALL, strLocale.c_str());
32     return wstrResult;
33 }

 

字符集(Charcater Set)与字符编码(Encoding)

字符集(Charcater
Set或Charset):
是一个系统辅助的具备抽象字符的聚众,也就是一体系字符的成团。字符是各样文字和标志的总称,包括各国家文字、标点符号、图形符号、数字等。常见的字符集有:ASCII字符集、GB2312字符集(首要用以拍卖中文汉字)、GBK字符集(紧要用于拍卖中文汉字)、Unicode字符集等。

字符编码(Character
Encoding):
是一套法则,使用该法则可以对自然语言的字符的一个字符集(如字母表或音节表),与统计机能识另外二进制数字举办杂交。即它能在符号集合与数字系统之间建立对应涉及,是信息处理的一项要旨技能。通常人们用符号集合(一般景观下就是文字)来发挥信息,而电脑的音讯处理系统则是以二进制的数字来囤积和拍卖新闻的。字符编码就是将标志转换为电脑能分辨的二进制编码。

发展历史,貌似一个字符集等同于一个编码形式,ANSI类别(ANSI是一种字符代码,为使统计机协助更多语言,常常采用0x80~0xFF 范围的 2 个字节来代表 1 个字符)的字符集如ASCII、ISO
8859-1、GB2312、GBK等等都是这样。一般大家说一种编码都是本着某一一定的字符集。 
一个字符集上也足以有多种编码模式,例如UCS字符集(也是Unicode使用的字符集)上有UTF-8、UTF-16、UTF-32等编码模式。

从统计机字符编码的前行历史角度来看,大概经历了六个级次: 
先是个等级:ASCII字符集和ASCII编码。 
总括机刚最先只扶助立陶宛语(即拉丁字符),此外语言不可知在电脑上囤积和展示。ASCII用一个字节(Byte)的7位(bit)表示一个字符,第一地点0。后来为了表示更多的非洲常用字符又对ASCII举行了增添,又有了EASCII,EASCII用8位表示一个字符,使它能多表示128个字符,扶助了一些西欧字符。

第二个等级:ANSI编码(本地化) 
为使统计机扶助更多语言,通常使用
0x80~0xFF 范围的 2 个字节来代表 1 个字符。比如:汉字 ‘中’
在普通话言操作系统中,使用 [0xD6,0xD0] 这多少个字节存储。 
不同的国度和地域制定了不同的规范,由此发出了
GB2312, BIG5, JIS 等分其它编码标准。那么些应用 2
个字节来表示一个字符的各样汉字延伸编码情势,称为 ANSI
编码。在简体中文系统下,ANSI 编码代表 GB2312
编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 
不同 ANSI
编码之间互不兼容,当信息在列国间交流时,不可以将属于两种语言的文字,存储在同一段
ANSI 编码的文本中。

其三个阶段:UNICODE(国际化) 
为了使国际间音讯沟通进一步方便,国际集团制订了
UNICODE
字符集,为各样语言中的每一个字符设定了合并并且唯一的数字编号,以满意跨语言、跨平台开展文本转换、处理的渴求。UNICODE
常见的有两种编码情势:UTF-8(1个字节表示)、UTF-16((2个字节表示))、UTF-32(4个字节表示)。

咱俩得以用一个树状图来表示由ASCII发展而来的依次字符集和编码的分支: 

发展历史 1

图1.各样类型的编译

假诺要更详尽地询问字符集和字符编码请参考:

字符集和字符编码(Charset &
Encoding)

 

工程里多字节字符与宽字符的配置

右键你的工程名->Properties,设置如下: 

发展历史 2

图2.Character Set

 

1、当设置为Use Unicode Character
Set时,会有预编译宏:_UNICODE、UNICODE

发展历史 3

图3.Unicode

 

2、当设置为Use Multi-Byte Character
Set时,会有预编译宏:_MBCS 

发展历史 4

图4.Multi-Byte

 

Unicode Character
Set与Multi-Byte Character Set有咋样界别吗?

Unicode Character Set和Multi-Byte
Character Set这多个设置有什么样界别吧?我们来看一个事例: 
有一个主次需要用MessageBox弹出提醒框:

1 #include "windows.h"
2 
3 void TestMessageBox()
4 {
5     ::MessageBox(NULL, "这是一个测试程序!", "Title", MB_OK);
6 }

上边这多少个Demo相当简单不用多说了啊!大家将Character
Set设置为Multi-Byte Character
Set时,可以正常编译和运行。但当大家设置为Unicode Character
Set,则会有以下编译错误:

error C2664: ‘MessageBoxW’ : cannot convert parameter 2 from ‘const char [18]’ to ‘LPCWSTR’

这是因为MessageBox有四个版本,一个MessageBoxW针对Unicode版的,一个是MessageBoxA针对Multi-Byte的,它们通过不同宏举办隔开,预设不同的宏会使用不同的本子。我们应用了Use
Unicode Character
Set就预设了_UNICODE、UNICODE宏,所以编译时就会利用MessageBoxW,这时我们传入多字节常量字符串肯定会有问题,而相应传入宽符的字符串,即将”Title”改为L”Title”就足以了,”这是一个测试程序!”也一样。

WINUSERAPI
int
WINAPI
MessageBoxA(
    __in_opt HWND hWnd,
    __in_opt LPCSTR lpText,
    __in_opt LPCSTR lpCaption,
    __in UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
    __in_opt HWND hWnd,
    __in_opt LPCWSTR lpText,
    __in_opt LPCWSTR lpCaption,
    __in UINT uType);
#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

下面的Multi-Byte Character
Set一般是指ANSI(多字节)字符集,关于ANSI请参见第二小节“字符集(Charcater
Set)与字符编码(Encoding)”。而Unicode Character
Set就是Unicode字符集,一般是指UTF-16编码的Unicode。也就是说每个字符编码为六个字节,四个字节能够象征65535个字符,65535个字符可以象征世界上绝大多数的语言。

相似推荐应用Unicode的法门,因为它能够适应各种国家语言,在进展软件国际时将会要命便得。除非在对存储要求相当高的时候,或要兼容C的代码时,我们才会拔取多字节的格局。

 

理解_T()、_Text()宏及L” “

上一小节对MessageBox的调用中除去使用L”Title”外,还足以行使_T(“Title”)和_TEXT(“Title”)。而且你会发觉在MFC和Win32顺序中会更多地应用_T和_TEXT,那_T、_TEXT和L之间有什么样区别吗?

 通过第一小节多字节字符与宽字节字符我们精通表示多字节字符(char)串常量时用一般的双引号括起来就足以了,如”String
test”;而表示宽字节字符(wchar_t)串常量时要在引号前加L,如L”String
test”。

 

翻开tchar.h头文件的概念大家知道_T和_TEXT的功用是一致的,是一个预定义的宏。

1 #define _T(x)       __T(x)
2 #define _TEXT(x)    __T(x)

咱俩再看看__T(x)的定义,发现它有两个:

1 #ifdef  _UNICODE
2 // ... 省略其它代码
3 #define __T(x)      L ## x
4 // ... 省略其它代码
5 #else   /* ndef _UNICODE */
6 // ... 省略其它代码
7 #define __T(x)      x
8 // ... 省略其它代码
9 #endif  /* _UNICODE */

这下领悟了啊?当我们的工程的Character
Set设置为Use Unicode Character
Set时_T和_TEXT就会在常量字符串后面加L,否则(即Use Multi-Byte Character
Set时)就会以一般的字符串处理。

 

Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR

VC++中还有局部常用的宏你也许会范糊涂,如Dword、LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR。这里我们联合总结一下:

周边的宏:

类型 MBCS UNICODE
WCHAR wchar_t wchar_t
LPSTR char* char*
LPCSTR const char* const char*
LPWSTR wchar_t* wchar_t*
LPCWSTR const wchar_t* const wchar_t*
TCHAR char wchar_t
LPTSTR TCHAR*(或char*) TCHAR* (或wchar_t*)
LPCTSTR const TCHAR* const TCHAR*

P表示这是一个指针。

T表示_T宏,那么些宏用来代表您的字符是否采用UNICODE,
假如你的程序定义了UNICODE或者其他有关的宏,那么那些字符或者字符串将被当作UNICODE字符串,否则就是正经的ANSI字符串。

STR代表这一个变量是一个字符串。

C表示是一个常量,const。

LPTSTR:LPSTR、LPWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI

LPCTSTR: LPCSTR、LPCWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI

 1 #ifdef UNICODE
 2 
 3     typedef LPWSTR LPTSTR;
 4 
 5     typedef LPCWSTR LPCTSTR;
 6 
 7 #else
 8 
 9     typedef LPSTR LPTSTR;
10 
11     typedef LPCSTR LPCTSTR;
12 
13 #endif 

 

互动转换方法: 
LPWSTR->LPTSTR: W2T(); 
LPTSTR->LPWSTR: T2W(); 
LPCWSTR->LPCSTR: W2CT(); 
LPCSTR->LPCWSTR: T2CW();

ANSI->UNICODE: A2W(); 
UNICODE->ANSI: W2A();

 

字符串函数: 
还有部分字符串的操作函数,它们也有一
一对应提到:

MBCS UNICODE
strlen(); wcslen();
strcpy(); wcscpy();
strcmp(); wcscmp();
strcat(); wcscat();
strchr(); wcschr();

透过那个函数和宏的命名你恐怕就意识了部分原理,一般包含前缀w(或后缀W)的都是用来宽字符的,而不带前缀w(或带有后缀A)的貌似是用以多字节字符的。

 

通晓CString爆发的原委与做事的机理

CString:动态的TCHAR数组,是对TCHAR数组的一种封闭。它是一个完全独立的类,封装了“+”等操作符和字符串操作方法,换句话说就是CString是对TCHAR操作的形式的集纳。它的功力是有利WIN32程序和MFC程序举行字符串的处理和类另外变换。

 

转自《带您玩转Visual
Studio——带你知道多字节编码与Unicode码

发表评论

电子邮件地址不会被公开。 必填项已用*标注