用C++读写unicode文本

本文介绍了C++中处理Unicode编码的基本方法,包括字符类型、字符串表示与转换、以及文件I/O操作等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<!-- /* 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 文本了,写的方法类似。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值