【问题标题】:Issues with Wide Characters in C++C++ 中的宽字符问题
【发布时间】:2017-06-22 22:18:43
【问题描述】:

我有一个程序,旨在读取一个包含单词的文本文件(每个单词位于单独的一行),然后从该文件中打印出一个随机单词。它还使您能够选择非英语语言(例如希腊语或俄语)。由于后一种情况,我使用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++ 中宽字符的来龙去脉不是很熟悉,所以我无法真正辨别出哪里出了问题。有人有什么想法吗?

【问题讨论】:

  • 您确定将 Windows 控制台设置为以std:wstring 使用的编码显示字符吗?
  • 我确实将命令提示符中的 `chcp' 设置为 65001。仍然给了我字符串。
  • chcp 65001 启用(基本)UTF-8 解码。请参阅 Vada Poché 的回答。
  • 你看过 Joel Spolsky 的 Unicode 文章

标签: c++ wstring wchar


【解决方案1】:

我猜你正在使用 Visual Studio。这是在 Windows 中实现 std::basic_filebuf 的一个怪癖。来自the relevant MSDN page

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

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

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

【讨论】:

  • 我实际上在使用 MinGW,但这仍然是一个有趣的分析。
  • @MrM21632 如果即使在使用您接受的答案后仍出现错误(您确实对此发表了评论),那么可能 MinGW 实现使用相同的 Windows 实现 FILE 并且您应该尝试其中之一解决方法。
【解决方案2】:

创建实例化 wfstream 对象后,像这样调用 imbue

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

【讨论】:

  • 添加那行代码时出现错误,即 empty() 不是 std::locale 的成员。是的,我做了#include .
  • 我假设你还没有完成 #include &lt;codecvt&gt; 并且需要调用 std::codecvt_utf8&lt;T&gt;,我们在调用 fin.imbue() 时使用它
  • @VadaPoché std::locale::empty() 不是非标准扩展吗?
  • 好的,我已经成功地通过 Visual Studio 使用 `std::locale::empty()' 嵌入(我已经开始明确地在这个程序中使用它!)。我仍然有问题。我会在明天的某个时候用新的细节更新最初的帖子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多