多字节与宽字节
- 多字节字符集(MBCS,Multi-Byte Chactacter Set):指用多个字节来表示一个字符的字符编码集合。一般英文字母用1Byte,汉语等用2Byte来表示。兼容ASCII 127。
在最初的时候,Internet上只有一种字符集——ANSI的ASCII字符集,它使用7 bits来表示一个 字符,总共表示128个字符,其中包括了 英文字母、数字、标点符号等常用字符。
为了扩充ASCII编码,以用于显示本国的语言,不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码,又称为"MBCS(Muilti-Bytes Charecter Set,多字节字符集)"。
不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。一个很大的缺点是,同一个编码值,在不同的编码体系里代表着不同的字。这样就容易造成混乱。导致了unicode码的诞生。
- 宽字节字符集:一般指Unicode编码的字符集,
Unicode称为统一码或万国码,统一了不同国家的字符编码。
Unicode通常用两个字节表示一个字符,原有的英文编码从单字节变成双字节,只需要把高字节全部填为0就可以。
为了统一所有文字的编码,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode固然统一了编码方式,但是它的效率不高,比如UCS-4(Unicode的标准之一)规定用4个字节存储一个符号,那么每个英文字母前都必然有三个字节是0,这对存储和传输来说都很耗资源。为了提高Unicode的编码效率,于是就出现了UTF-8编码。UTF-8可以根据不同的符号自动选择编码的长短。比如英文字母可以只用1个字节就够了。
UTF是“Unicode Transformation Format”的缩写,可以翻译成Unicode字符集转换格式,即怎样将Unicode定义的数字转换成程序数据。用char、char16_t、char32_t分别表示无符号8位整数,无符号16位整数和无符号32位整数。UTF-8、UTF-16、UTF-32分别以char、char16_t、char32_t作为编码单位。(注: char16_t 和 char32_t 是 C++ 11 标准新增的关键字。如果你的编译器不支持 C++ 11 标准,请改用 unsigned short 和 unsigned long。)“汉字”的UTF-8编码需要3个字节。“汉字”的UTF-16编码需要两个char16_t,大小是2个字节。“汉字”的UTF-32编码需要两个char32_t,大小是4个字节。
普通字符、字符串前加 L 就变成宽字符 wchar_t 存储(用2Byte存1个字符)了,例如,L‘看’,L"abc啊";或_T("sf飞")
MFC中的 CString 与 std::string 的转换:
1. 使用Unicode字符集时,CString等价于CStringW;使用多字节字符集时,CString相对于CStringA
2. CString --> std::string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 1. Unicode下 CString --> std::string // 方法1 CString str = L "sdf" ; std::string s = CT2A(str.GetString()); // GetString()比较新的VS有,旧可以用GetBuffer() std::string s = CT2A(str.GetBuffer()); str.ReleaseBuffer(); // 方法2 CString str = L "dshf" ; CStringA stra(str); std::string s(stra); //或 std::string s(CStringA(str)); //方法3 USES_CONVERSION; CString str = L "djg" ; std::string s = W2A(str); //首先str--》const wchar_t* ,然后W2A将const wchar_t*--》const char*, //最后用const char*初始化s |
3. std::string --> CStringW / std::wstring
1
2
3
|
std::string s( "dhhh" ); CStringW strw(CStringA(s.c_str()); std::wstring sw(strw); |
1)TCHAR 转换为const wchar_t *,直接强制转换,在TCHAR前面加上(*const wchar_t)
2)BSTR:是一个OLECHAR*类型的Unicode字符串,是一个COM字符串,带长度前缀,与VB有关,没怎么用到过。
LPSTR:即 char *,指向以'/0'结尾的8位(单字节)ANSI字符数组指针
LPWSTR:即wchar_t *,指向'/0'结尾的16位(双字节)Unicode字符数组指针
LPCSTR:即const char *
LPCWSTR:即const wchar_t *
LPTSTR:LPSTR、LPWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI
LPCTSTR: LPCSTR、LPCWSTR两者二选一,取决于是否宏定义了UNICODE或ANSI,
如下是从MFC库中拷来的:
1
2
3
4
5
6
7
|
#ifdef UNICODE typedef LPWSTR LPTSTR ; typedef LPCWSTR LPCTSTR ; #else typedef LPSTR LPTSTR ; typedef LPCSTR LPCTSTR ; #endif |
相互转换方法:
LPWSTR->LPTSTR: W2T();
LPTSTR->LPWSTR: T2W();
LPCWSTR->LPCSTR: W2CT();
LPCSTR->LPCWSTR: T2CW();
ANSI->UNICODE: A2W();
UNICODE->ANSI: W2A();
3)
LPWSTR转为LPCSTR
LPCSTR=CW2A(LPWSTR);
4)
CString与LPCWSTR的转化(http://www.cnblogs.com/foolboy/archive/2005/07/25/199869.html)
问题起因:
在写WritePrivateProfileString写.ini配置文件时在msdn中看到,如果想要写的配置信息即时生效,必须在之前使用WritePrivateProfileStringW来re-read一下目标.ini文件,其原文如下:
1
2
3
4
|
// force the system to re-read the mapping into shared memory // so that future invocations of the application will see it // without the user having to reboot the system WritePrivateProfileStringW( NULL, NULL, NULL, L "appname.ini" ); |
查了一下msdn中WritePrivateProfileStringW的原型如下:
1
2
3
4
5
6
7
8
|
WINBASEAPI BOOL WINAPI WritePrivateProfileStringW ( LPCWSTR lpAppName, //section []中的字符串 LPCWSTR lpKeyName, // key “=”左边的字符串 LPCWSTR lpString, //写入的内容 LPCWSTR lpFileName ) // 配置文件的路径 例如: [section] key=string |
其中的每个参数的类型都为LPCWSTR,实际中获得的文件名都为CString,问题产生。
问题分析:
LPCWSTR 是Unicode字符串指针,初始化时串有多大,申请空间就有多大,以后存储若超过则出现无法预料的结果,这是它与CString的不同之处。而CString是一个串类,内存空间类会自动管理。LPCWSTR 初始化如下:
1
|
LPCWSTR Name=L "TestlpCwstr" ; |
由于LPCWSTR必须指向Unicode的字符串,问题的关键变成了Anis字符与Unicode字符之间的转换,不同编码间的转换,通过查找资料可知,可以ATL中转换宏可以用如下方法实现:
1
2
3
4
5
6
7
8
9
10
|
//方法一 CString str=_T( "TestStr" ); USES_CONVERSION; LPWSTR pwStr= new wchar_t [str.GetLength()+1]; wcscpy(pwStr,T2W(( LPCTSTR )str)); // 方法二 CString str=_T( "TestStr" ); USES_CONVERSION; LPWCSTR pwcStr = A2CW(( LPCSTR )str); |
MFC中CString和LPSTR是可以通用,其中A2CW表示(LPCSTR) -> (LPCWSTR),USER_CONVERSION表示用来定义一些中间变量,在使用ATL的转换宏之前必须定义该语句。
顺便也提一下,如果将LPCWSTR转换成CString,那就更加容易,在msdn中的CString类说明中提到了可以直接用LPCWSTR来构造CString,所以可以进行如下的转换代码:
LPCWSTR pcwStr = L"TestpwcStr";
CString str(pcwStr);
问题总结:
在头文件<atlconv.h>中定义了ATL提供的所有转换宏,如:
A2CW (LPCSTR) -> (LPCWSTR)
A2W (LPCSTR) -> (LPWSTR)
W2CA (LPCWSTR) -> (LPCSTR)
W2A (LPCWSTR) -> (LPSTR)
所有的宏如下表所示:
A2BSTR | OLE2A | T2A | W2A |
A2COLE | OLE2BSTR | T2BSTR | W2BSTR |
A2CT | OLE2CA | T2CA | W2CA |
A2CW | OLE2CT | T2COLE | W2COLE |
A2OLE | OLE2CW | T2CW | W2CT |
A2T | OLE2T | T2OLE | W2OLE |
A2W | OLE2W | T2W | W2T |
上表中的宏函数,非常的有规律,每个字母都有确切的含义如下:
2 | to 的发音和 2 一样,所以借用来表示“转换为、转换到”的含义。 |
A | ANSI 字符串,也就是 MBCS。 |
W、OLE | 宽字符串,也就是 UNICODE。 |
T | 中间类型T。如果定义了 _UNICODE,则T表示W;如果定义了 _MBCS,则T表示A |
C | const 的缩写 |
利用这些宏,可以快速的进行各种字符间的转换。使用前必须包含头文件,并且申明USER_CONVERSION;使用 ATL 转换宏,由于不用释放临时空间,所以使用起来非常方便。但是考虑到栈空间的尺寸(VC 默认2M),使用时要注意几点:
1、只适合于进行短字符串的转换;
2、不要试图在一个次数比较多的循环体内进行转换;
3、不要试图对字符型文件内容进行转换,因为文件尺寸一般情况下是比较大的;
4、对情况 2 和 3,要使用 MultiByteToWideChar() 和 WideCharToMultiByte();
MultiByteToWideChar() 和 WideCharToMultiByte()的用法:
www.cnblogs.com/ranjiewen/p/5770639.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
int MultiByteToWideChar( UINT CodePage, //指定执行转换的多字节字符所使用的字符集,CP_ACP:ANSI字符集,CP_UTF8:UTF-8字符集 DWORD dwFlags, // 一般为NULL LPCSTR lpMultiByteStr, // [in] 要被转换的字符指针 int cchMultiByte, // lpMultiByteStr指针指向的字符串的长度,若字符串以\0结尾,可简单写为 -1 LPWSTR lpWideCharStr, //[out] 输出的宽字符串指针 int cchWideChar // 指定由参数lpWideCharStr指向的缓冲区的宽字符数。若此值为0,函数不会执行转换,而是返回目标缓存lpWideChatStr所需的宽字符数。 ); int WideCharToMultiByte( UINT CodePage, //指定执行转换的字符集 DWORD dwFlags, // NULL LPCWSTR lpWideCharStr, // 待转换的字符串 int cchWideChar, // 待转换的字符串长度,若以空字符结尾,则可写-1 LPSTR lpMultiByteStr, // 指向接收被转换字符串的缓冲区 int cbMultiByte, // 缓冲区的长度,若为0,函数返回接收的缓冲区的长度 LPCSTR lpDefaultChar, // NULL LPBOOL lpUsedDefaultChar //NULL ); |
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
#include <Windows.h> /// std::string ==> std::wstring std::wstring s2ws(std::string s) { //CP_ACP : ANSI字符集 //当cchWideChar=0,返回存宽字符的长度, //并且待转换的字符串的长度为 -1 时,返回的长度包括空字符\0,new时 new wchar_t[nLen] //待转换的字符串的长度为 s.size() 时,返回的长度不包括空字符\0,new时 new wchar_t[nLen+1] // 1. 用 -1 int nLen = ::MultiByteToWideChar(CP_ACP, NULL, s.c_str(), -1, NULL, 0); wchar_t *buf = new wchar_t [nLen]; //wmemset(buf, 0, nLen);//当转换包括\0,就不用初始化0了 ::MultiByteToWideChar(CP_ACP, NULL, s.c_str(), -1, buf, nLen); std::wstring ws(buf); delete [] buf; return ws; // 2. 用 s.size() int nLen = ::MultiByteToWideChar(CP_ACP, NULL, s.c_str(), s.size(), NULL, 0); wchar_t *buf = new wchar_t [nLen+1]; wmemset(buf, 0, nLen+1); //当转换包括\0,就不用初始化0了 ::MultiByteToWideChar(CP_ACP, NULL, s.c_str(), s.size(), buf, nLen); std::wstring ws(buf); delete [] buf; return ws; } /// std::wstring ==> std::string std::string ws2s(std::wstring ws) { int nLen = ::WideCharToMultiByte(CP_ACP, NULL, ws.c_str(), -1, NULL, 0, NULL, NULL); char * buf = new char [nLen]; ::WideCharToMultiByte(CP_ACP, NULL, ws.c_str(), -1, buf, nLen, NULL, NULL); std::string s(buf); delete [] buf; return s; } ///// 当需要转换不同字符集(ANSI:CP_ACP UTF8:CP_UTF8)时, ///// 就必须用WideCharToMultiByte和MultiByteToWideChar (暂时没找到别的,高手请指教) // ANSI ==> UTF8 std::string ANSI_to_UTF8(std::string sAnsi) { std::wstring wsAnsi = s2ws(sAnsi); int nLen = ::WideCharToMultiByte(CP_UTF8, NULL, wsAnsi.c_str(), -1, NULL, 0, NULL, NULL); char * buf = new char [nLen]; ::WideCharToMultiByte(CP_UTF8, NULL, wsAnsi.c_str(), -1, buf, nLen, NULL, NULL); std::string sUtf8(buf); delete [] buf; return sUtf8; } // UTF8 ==> ANSI std::string UTF8_to_ANSI(std::string sUtf8) { //std::wstring wsUtf8 = s2ws(sUtf8);//不能用这句,因为这是ANSI字符集的转换 int nLen = ::MultiByteToWideChar(CP_UTF8, NULL, sUtf8.c_str(), -1, NULL, 0); wchar_t *wbuf = new wchar_t [nLen]; ::MultiByteToWideChar(CP_UTF8, NULL, sUtf8.c_str(), -1, wbuf, nLen); std::wstring wsUtf8(wbuf); delete [] wbuf; //int nLen2 = ::WideCharToMultiByte(CP_ACP, NULL, wsUtf8.c_str(), -1, NULL, 0, NULL, NULL); //char* buf = new char[nLen2]; //::WideCharToMultiByte(CP_ACP, NULL, wsUtf8.c_str(), -1, buf, nLen2, NULL, NULL); //std::string sAnsi(buf); //delete[] buf; //或者 std::string sAnsi = ws2s(wsUtf8); return sAnsi; } int main( int argc, char * argv[]) { std::string s( "Hello world.你好,中国。" ); std::wstring ws = s2ws(s); std::string s1 = ws2s(ws); std::string sAnsi(s); std::string sUtf8 = ANSI_to_UTF8(sAnsi); std::string sAnsi2 = UTF8_to_ANSI(sUtf8); std::ofstream file( "1.txt" ); file << sUtf8.c_str(); return 0; } |
上面的函数整理:
采用ATL封装_bstr_t的过渡:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <comutil.h> #pragma comment(lib, "comsuppw.lib") string ws2s( const wstring& ws) { _bstr_t t = ws.c_str(); char * pchar = ( char *)t; string result = pchar; return result; } wstring s2ws( const string& s) { _bstr_t t = s.c_str(); wchar_t * pwchar = ( wchar_t *)t; wstring result = pwchar; return result; } --------------------- 原文:https: //blog.csdn.net/liminwang0311/article/details/79975174 |
使用MFC的CString:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <atlstr.h> std::string ws2s(std::wstring ws) { return std::string(CStringA(CStringW(ws.c_str()))); } std::wstring s2ws(std::string s) { return std::wstring(CStringW(CStringA(s.c_str()))); } //其实 ws => const wchar_t* => CStringW => LPCWSTR => CStringA => LPCSTR => string string s(CStringA(CStringW(ws.c_str())); wstring ws(CStringW(CStringA(s.c_str())); //其中 CStringW => LPCWSTR 和 CStringA => LPCSTR 是默认自动转换的。定义了 operator LPCSTR() const LPCSTR pStr = "kkk" ; //LPCSTR == const char* LPCWSTR pwStr = L "hhh" ; //LPCWSTR == const wchar_t* CStringA a(pwStr); //"hhh" CStringW w(pStr); //L"kkk" |