<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-format:other; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:宋体; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:36.0pt 36.0pt 36.0pt 36.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} -->
熟悉一下字符类型, char, wchar_t, TCHAR ,最熟悉的 char 是单字节字符,适用于 ANSI 编码; wchar_t 是双字节的宽字符类型,适用于 unicode 编码; TCHAR 是一个宏,在 ANSI 坏境下定义为 char , unicode 坏境下定义为 wchar_t 。
怎么来表示字符串?对,字符数组,要知道在 C++ 语言里面,其实没有数组的数据结构,所谓数组,都是由指针 + 长度来表示。字符型指针 const char *, const wchar_t *, const TCHAR * 可以用来在不同的环境下表示字符串。再说相关的几个宏, LPSTR: long point string, 相当于 char *; LPCSTR: long point const string, 相当于 const char *; LPCWSTR: long point const wide string, 相当于 const wchar_t *; LPCTSTR: 类似的,相当于 const TCHAR *; 这些都不要死记硬背,记着大写字母的意思即可猜出其含义。
一个字符串,比如说 " 北京 2008" ,对应 ANSI 编码表示为 const char * cha = " 北京 2008"; unicode 编码表示为 const wchar_t * wcha = L" 北京 2008"; 。在内存里以二进制存储, ANSI 编码对应为 0x B1B1 BEA9 32 30 30 38 , unicode 编码为 0x 1753 AC4E 3200 3000 3000 3800 。
回到上面,为什么字符型指针可以表示一个字符串?计算机找到这个指针,只能知道串首字符,这里因为字符串有个默认的结束符 '/0' ( ANSI 或者 ASCII 表示为 0x00 ),从首字符开始,计算机开始向后查找直到 0x00 ,认为字符串结束,所以存储字符串的时候,计算机是带着一个特殊结束符的。可是要注意了,这个结束符 0x00 是 ASCII 码定义的结束符啊,那么在宽字符 unicode 环境下呢?结束符是什么?是 0x0000 。
而对于非 const 字符串,怎么表示? char * 方法怎么动态定义长度?好办,可以用 new 手动分配内存空间,除此之外,还有更好办的方法,那就是字符串类型 string, 怎么可变长度,怎么记录长度,内存怎么存储,这些都不用管,都有 C++ 标准库自动管理。
不同类型的字符串间之间怎么转换?比如定义 char * cha; string str; str = cha; // 可以实现 char * 到 string 的转换, cha = str.c_str(); 可以从 string 转换到 char * ;对于 wchar_t wcha; wstring wstr; 呢? wstr = wcha; wcha = wstr.c_str(); // 这个是否可以呢?!
说过了字符串的表示和类型转换,再来看字符流 I/O , C++ 里面的 fstream, ifstream, ofstream, 文件流的 I/O 有好多种方式,默认为字符流方式,明确的说是 ANSI 字符流,都是针对 ANSI 文本的,那么 unicode 怎么读写呢?
C++ 里倒真有 wfsteam 流的,可惜用起来也很奇怪,用 wifstream 读取 unicode 文本,结果竟然是读取一个字节,加上一个 0x00 ,在读取下一个字节,如此!比如文本里保存的还是 “ 北京 2008” ,刚才说过 unicode 编码为 0x 1753 AC4E 3200 3000 3000 3800 ;用 wifstream 读到内存的字符竟是 0x 1700 5300 AC00 4E00 ... 这叫什么 unicode ?我不知道 wfstream 怎么正确使用用,有知道的朋友还请不吝告知!
既然 wftream 不行,那么怎么读取 unicode 呢,这里可以借鉴一下二进制流的读写方式,二进制流在读写时必须明白存储单位的数据结构,定义为结构体,然后逐 n 字节( n 为结构长度)按二进制读取;这个可以借鉴过来,不用定义结构了,直接用 wchar_t ,代码如下:
ifstream fin;
fin.open(filename, ios::binary);
// 跳过 unicode 文本开头有两个字节 0xFFFE (称作 BOM ,用于标识 unicode 编码)
fin.seek(2, ios::beg);
while (!fin.eof())
{
wchar_t wch;
fin.read((char *)(&wch), 2);
}
如果要按行读取,怎么办?好了,有 ifstream 的成员函数 getline(cha, size) ,还有 string 类成员函数 getline(fin, str) 。你试试能不能用在 unicode 下使用?答案是否定的!为什么?因为 getline 函数默认在 ANSI 下使用,它对换行符的判断是基于 ASCII 码的换行( 0x0D )和行开头标记( 0x0A ),如果把它用在 unicode 编码下,比如 “ 不 ” 字, unicode 编码为 0x0D4E 。当 getline 函数执行到这,以为换行了,所以说会失效!那么 unicode 换行符以及行开头符的二进制是什么?双字节了,是 0x0D00 和 0x0A00 ,这时候 getline 函数就失效了,怎么办,手动判断:
ifstream fin;
fin.open(filename, ios::binary);
size_t index = 2;
while (!fin.eof())
{
fin.seekg(index, ios::beg);
wchar_t wch;
fin.read((char *)(&wch), 2);
if (wch == 0x000D) // 判断回车
{
strLineAnsi = ws2s(wstrLine);
wstrLine.erase(0, wstrLine.size() + 1);
iLine++;
index += 4; // 跳过回车符和行开头符
}
else
{
wstrLine.append(1, wch);
index += 2;
}
}
上面的程序可以读取 unicode 了,那么读了进来怎么理解 unicode 呢,这就需要 char * 和 wchar_t * 间的转换了,这个没有简便的方法, ANSI 、 UNICODE 两种编码之间的转换,只能靠查表实现, C++ 提供了两个函数, wcstombs(_Dest, _Source, _Dsize) 从 unicode 编码转化为 ANSI 编码 , mbstowcs(_Dest, _Source, _Dsize) 反之,参数对应为 const char*, const wchar_t* 以及长度。这里在提供一个网上的函数,用于实现 string 和 wstring 的转换:
std::string ws2s(const std::wstring& ws)
{
std::string curLocale = setlocale(LC_ALL, NULL); // curLocale = "C";
setlocale(LC_ALL, "chs");
const wchar_t* _Source = ws.c_str();
size_t _Dsize = 2 * ws.size() + 1;
char *_Dest = new char[_Dsize];
memset(_Dest,0,_Dsize);
wcstombs(_Dest,_Source,_Dsize);
std::string result = _Dest;
delete []_Dest;
setlocale(LC_ALL, curLocale.c_str());
return result;
}
std::wstring s2ws(const std::string& s)
{
setlocale(LC_ALL, "chs");
const char* _Source = s.c_str();
size_t _Dsize = s.size() + 1;
wchar_t *_Dest = new wchar_t[_Dsize];
wmemset(_Dest, 0, _Dsize);
mbstowcs(_Dest,_Source,_Dsize);
std::wstring result = _Dest;
delete []_Dest;
setlocale(LC_ALL, "C");
return result;
}
写到这里,就可以用 C++ 读取 unicode 文本了,写的方法类似。