C++中宽字符的问题

Issues with Wide Characters in C++

本文关键字:问题 字符 C++      更新时间:2023-10-16

我有一个程序,它旨在读取单词的文本文件(每个单词都在单独的一行),然后从该文件中打印出一个随机单词。它还使您能够选择非英语语言(如希腊语或俄语)。由于后一种情况,我使用std::wstring来捕获文本。这是代码:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/random_device.hpp>
#include <boost/random/uniform_int_distribution.hpp>

int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "Usage: word [lang]" << std::endl;
std::cout << "tlang: Choose from de,en,es,fr,gr,it,la,ru" << std::endl;
return EXIT_FAILURE;
}
std::string file = static_cast<std::string>("C:\util_bin\data\words_") + static_cast<std::string>(argv[1]) + static_cast<std::string>(".txt");
std::wfstream fin(file, std::wifstream::in);
std::vector<std::wstring> data;
std::wstring line;
while (std::getline(fin, line))
data.push_back(line);
int size = data.size();
boost::random::random_device rd;
boost::random::mt19937 mt(rd());
boost::random::uniform_int_distribution<int> dist(0, size - 1);
std::wcout << data[dist(mt)] << std::endl;
}

这段代码编译得很好,但当我用俄语运行它时(例如),我只得到垃圾文本:

C:util_bin>word ru
������������
C:util_bin>

我不太熟悉C++中宽字符的来龙去脉,所以我真的看不出哪里出了问题。有人有什么想法吗?

我猜您使用的是Visual Studio。这是std::basic_filebuf在Windows中实现的一个怪癖。来自相关MSDN页面:

basic_filebuf类型的对象使用类型为char *的内部缓冲区创建,而与类型参数Elem指定的char_type无关。这意味着在将Unicode字符串(包含wchar_t字符)写入内部缓冲区之前,它将被转换为ANSI字符串(包含char字符)。要在缓冲区中存储Unicode字符串,请创建一个类型为wchar_t的新缓冲区,并使用basic_streambuf::pubsetbuf()方法进行设置。

正如我所解释的,filebuf是用FILE*实现的;有一个内部标志可以执行ANSI转换,无论您是否愿意,但无法清除。除了通过分配和设置您自己的缓冲区(通过pubsetbuf)之外的标志。将codecvt放在您的区域设置中是不行的。它必须在成功打开文件后立即发生。真的,令人愤怒的侵扰。我最终不得不编写一个包装类(这还不错,因为它让您能够在打开之前存储文件名)。

您也可以使用std::binary打开该文件。有些人建议你总是这样做。但是以这种方式打开文件可能会使您在插入流或从流中提取之前进行自己的代码转换

创建实例化wfstream对象后,对其调用imbue,如下所示:

fin.imbue( std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>) );