C++读取unicode文本

本文详细介绍了C++中字符类型(char, wchar_t, TCHAR)及其在不同编码环境下的应用,包括如何表示字符串、字符数组、字符指针、字符串类型(char*, wchar_t*, TCHAR)以及不同字符串类型之间的转换方法。

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

<致敬原作者>http://librawill.blogspot.com/2008/08/cunicode_2881.html
熟悉一下字符类型,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文本了,写的方法类似。


 

### C++ 程序读取文本文档错误解决方案 当遇到C++程序读取文本文档时出现乱码的情况,通常是因为文件编码与编译器或运行环境不匹配造成的。以下是详细的解决方案: #### 文件编码一致性调整 确保文本文件和编程环境中使用的字符集一致非常重要。如果文档是以UTF-8格式保存,则应该让C++代码也按照相同的方式处理字符串数据[^1]。 对于Visual Studio开发环境下,默认情况下可能采用ANSI或其他本地化设置来解析输入流中的字符序列。因此,在尝试修改源文件本身的编码之前,先确认IDE内部设定是否支持多字节字符集(MBCS),或者更推荐地切换至Unicode模式下工作[^3]。 #### 修改文件编码方法 可以通过编辑器菜单栏操作更改现有TXT文档的编码形式: 1. 打开目标文本; 2. 寻找并点击“文件”选项卡下的“另存为...” 3. 查看底部区域是否有提及当前选用何种编码标准,并将其转换成适合项目需求的形式——例如从原始状态(可能是GBK/Big5等)转变为普遍兼容性强的标准如ASCII、UTF-8无BOM版本等;注意选择合适的编码方式以适应具体应用场景的要求。 #### 使用宽字符流进行IO操作 考虑到部分操作系统平台对窄字符的支持有限制,建议利用`<locale>`库配合wifstream/wofstream来进行跨平台稳定性的增强。通过适当配置区域性和字符映射表,能够有效减少由于不同系统间差异所带来的影响[^2]。 ```cpp #include <iostream> #include <fstream> #include <string> using namespace std; int main() { wifstream file(L"example.txt"); // 设置正确的区域信息 locale loc("chs"); file.imbue(loc); wchar_t ch; while (file.get(ch)) { wcout << ch; } return 0; } ``` 上述例子展示了如何借助于Windows系统的中文语言包(`chs`)完成对含有复杂脚本文字资源的有效访问过程。当然也可以根据实际情况替换为其他相应地区标识符。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值