字符串表示与转换
windows 编程中,存在几种字符串表示方式,包括:
1)C-Style 字符串 char* / wchar_t*;
2)C++ STL 字符串 std::string / std::wstring;
3) ATL/MFC 字符串 CStringA / CStringW;
以上每个版本都有对应的多字节(MBCS)与Unicode(DBCS)表示,其中多字节存在不同编码方案,使用代码页区分。在 windows 系统中,常见代码页有:
1)MS-DOS Latin US, code page 437,使用一个八位字节表示拉丁字符;
2)windows code page 1251 for Cyrillic, 使用一个八位字节表示斯拉夫字符;
3)code page 936/950/932/949 for ideographic,分别表示 简体中文/繁体中文/日语/韩语,使用一个八位字符表示拉丁字符,使用两个八位字符表示象形文字;
针对象形文字,使用 strlen 函数求字符串长度是没有意义的,strlen 函数简单返回字符串中以 '\0' 结尾前字符个数。
Unicode 使用两个字节表示一个字符,前128位与 ASCII(American Standard Code for Information Interchange) 保持一致。在使用非象形文字时语言时,有一半空间被浪费,故基于 Unicode 字符进行在编码,形成 utf8。这样,针对拉丁语系,斯拉夫语系等仅需要一个字符表示 Unicode 编码,而象形文字使用2,3,4个字节编码,具体见博客 ”字符集及编码“。在 visual c++开发中,utf8 编码可看作一种特殊代码页,使用函数 WideCharToMultiByte(CP_UTF8,...) 可以得到。
如何维护在一个源文件中维护两种编码方案(MBCS & UNICODE)?
winows 系统如下定义:
1 typedef wchar_t WCHAR; // wc, 16-bit UNICODE character 2 3 #ifdef UNICODE 4 // PTCHAR is a pointer to WCHAR 5 typedef WCHAR TCHAR, *PTCHAR; 6 #else 7 // PTCHAR is a pointer to char 8 typedef char TCHAR, *PTCHAR; 9 #endif 10 11 #ifdef _UNICODE 12 #define _tcscpy wcscpy 13 #define _tcslen wcslen 14 ... 15 #else 16 #define _tcscpy strcpy 17 #define _tcslen strlen 18 ... 19 #endif 20 21 #ifdef _UNICODE 22 #define __T(x) L ## x 23 #define __TEXT(quote) L##quote 24 #else 25 #define __T(x) x 26 #define __TEXT(quote) quote 27 #endif 28 29 // _T(x) __T(x) TEXT(x) _TEXT(x) __TEXT(x) same meaning 30 #define _T(x) __T(x) 31 #define _TEXT(x) __T(x) 32 #define TEXT(quote) __TEXT(quote)
根据以上定义,可以在使用一个源文件维护 C-Style 字符串,具体如下:
1 TCHAR *sz = _T("ch中国"); 2 int iLen = _tcslen(sz); 3 TCHAR *sz2 = new TCHAR[iLen + 1]; 4 _tcscpy(sz2, sz); 5 delete []sz2;
在项目 Properties->Configuration Prooperties->General 下选择 Use Multi-Byte Character Set(or Not Set ),iLen 值为 6,其中每个汉字使用两个字节;当选择 Use Unicode Character Set 时,iLen 值为 4, 表示 sz 有 4 个字符。
C++ STL 字符串 与 ATL/MFC 字符串是对 TCHAR 字符数据进行封装,表示如下:
1 // c++ string 2 typedef basic_string<char, char_traits<char>, allocator<char> > 3 string; 4 typedef basic_string<wchar_t, char_traits<wchar_t>, 5 allocator<wchar_t> > wstring; 6 7 // atl/mfc string 8 typedef ATL::CStringT< wchar_t, StrTraitMFC_DLL< wchar_t > > CStringW; 9 typedef ATL::CStringT< char, StrTraitMFC_DLL< char > > CStringA; 10 typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString; 11 12 // 由于c++ string 没有提供一个 TCHAR 版本,通过增加如下定义, 13 // 实现多字节与Unicode自动转换 14 #ifdef _UNICODE 15 #define tstring std::wstring 16 #else 17 #define tstring std::string 18 #endif
通过以上定义,使用 tstring(自定义),CString 可以在一个源文件中维护两种类型字符串。windows 也允许使用特定字符集字符串,提供了 MBCS/Unicode 之间转换函数,具体如下:
1 // char* -> wchar_t* 2 //char *sz1 = "ch中国"; 3 // 获取 sz1 字符串转换为 Unicode 长度 4 int iLen = MultiByteToWideChar(CP_ACP, 0, sz1, strlen(sz1), NULL, 0); 5 wchar_t* sz2 = new wchar_t[iLen + 1]; 6 MultiByteToWideChar(CP_ACP, 0, sz1, strlen(sz1), sz2, iLen); 7 sz2[iLen] = '\0'; // 以 '\0' 结尾字符串 8 9 // wchar_t* -> char* 10 wchar_t* sz3 = L"ch中国"; 11 // 获取 sz3 字符串转换为 多字节 长度 12 int iLen2 = WideCharToMultiByte(CP_ACP, 0, sz3, wcslen(sz3), NULL, 0, NULL, NULL); 13 char *sz4 = new char[iLen2 + 1]; 14 WideCharToMultiByte(CP_ACP, 0, sz3, wcslen(sz3), sz4, iLen2, NULL, NULL); 15 sz4[iLen2] = '\0'; // 可不使用 L 标志,因为单个字符系统可以零补齐 16 17 // CStringA -> CStringW 18 CStringA szA("ch中国"); 19 CStringW szW(szA); 20 21 // CStringW -> CStringA 22 CStringW szW2(L"ch中国"); 23 CStringA szA2(szW2); 24 25 26 // unicode -> utf8 -> Unicode 27 // vs 默认使用 CP_ACP 翻译多字节,当需要查看 utf8 编码字符串时,可在 watch 窗口添加 ,s8 查看 28 wchar_t *szUtf16 = L"ch中国"; 29 int len = WideCharToMultiByte(CP_UTF8, 0, szUtf16, wcslen(szUtf16), NULL, 0, NULL, NULL); 30 char *szUtf8 = new char[len + 1]; 31 WideCharToMultiByte(CP_UTF8, 0, szUtf16, wcslen(szUtf16), szUtf8, len, NULL, NULL); 32 szUtf8[len] = '\0'; 33 int len2 = MultiByteToWideChar(CP_UTF8, 0, szUtf8, strlen(szUtf8), NULL, 0); 34 wchar_t *szUtf16_2 = new wchar_t[len2 + 1]; 35 MultiByteToWideChar(CP_UTF8, 0, szUtf8, strlen(szUtf8), szUtf16_2, len2); 36 szUtf16_2[len2] = '\0';
编程过程中经常使用不同类型字符串,如何在不同类型字符串见转换呢?代码如下:
1 // TCHAR* <-> CString 2 TCHAR *sz = _T("ch中国"); 3 CString cstr(sz); 4 TCHAR* sz2 = cstr.GetBuffer(); 5 6 // TCHAR* <-> tsring 7 tstring tstr(sz); 8 const TCHAR* sz3 = tstr.c_str(); // tstr.data() is also ok 9 10 // CString <-> tstring 11 CString cstr2; 12 tstring tstr2; 13 tstr2 = cstr.GetBuffer(); 14 cstr2 = tstr.c_str();