【问题标题】:How to handle multiple locales for ifstream, cout, etc, in c++如何在 C++ 中处理 ifstream、cout 等的多个语言环境
【发布时间】:2018-04-15 09:43:19
【问题描述】:

我正在尝试读取和处理多个不同编码的文件。我应该只为此使用 STL。 假设我们有 iso-8859-15UTF-8 文件。

this SO 中回答说:

简而言之,对您来说更有趣的部分:

  1. std::stream (stringstream, fstream, cin, cout) 有一个内部 locale-object,它与全局 C++ 语言环境的值匹配 创建流对象的时刻。正如std::in 是 在调用 main 中的代码之前很久就创建了它,它拥有最多 可能是经典的 C 语言环境,无论您之后做什么。
  2. 您可以确保 std::stream 对象具有所需的 通过调用语言环境 std::stream::imbue(std::locale(your_favorite_locale))

问题在于,在这两种类型中,只有与首先创建的语言环境匹配的文件才能被正确处理。例如,如果 locale_DE_ISO885915 位于 locale_DE_UTF8 之前,则 UTF-8 中的文件不会正确附加到 string s 中,当我将 cout 取出时,我只会看到文件中的几行。

void processFiles() {
    //setup locales for file decoding
    std::locale locale_DE_ISO885915("de_DE.iso885915@euro");
    std::locale locale_DE_UTF8("de_DE.UTF-8");
    //std::locale::global(locale_DE_ISO885915);
    //std::cout.imbue(std::locale());
    const std::ctype<wchar_t>& facet_DE_ISO885915 = std::use_facet<std::ctype<wchar_t>>(locale_DE_ISO885915);
    //std::locale::global(locale_DE_UTF8);
    //std::cout.imbue(std::locale());
    const std::ctype<wchar_t>& facet_DE_UTF8 = std::use_facet<std::ctype<wchar_t>>(locale_DE_UTF8);

    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
    std::string currFile, fileStr;
    std::wifstream inFile;
    std::wstring s;

    for (std::vector<std::string>::const_iterator fci = files.begin(); fci != files.end(); ++fci) {
        currFile = *fci;

        //check file and set locale
        if (currFile.find("-8.txt") != std::string::npos) {
            std::locale::global(locale_DE_ISO885915);
            std::cout.imbue(locale_DE_ISO885915);
        }
        else {
            std::locale::global(locale_DE_UTF8);
            std::cout.imbue(locale_DE_UTF8);
        }

        inFile.open(path + currFile, std::ios_base::binary);
        if (!inFile) {
            //TODO specific file report
            std::cerr << "Failed to open file " << *fci << std::endl;
            exit(1);
        }

        s.clear();
        //read file content
        std::wstring line;
        while( (inFile.good()) && std::getline(inFile, line) ) {
            s.append(line + L"\n");
        }
        inFile.close();

        //remove punctuation, numbers, tolower...
        for (unsigned int i = 0; i < s.length(); ++i) {
            if (ispunct(s[i]) || isdigit(s[i]))
                s[i] = L' ';
        }

        if (currFile.find("-8.txt") != std::string::npos) {
            facet_DE_ISO885915.tolower(&s[0], &s[0] + s.size());
        }
        else {
            facet_DE_UTF8.tolower(&s[0], &s[0] + s.size());
        }
        fileStr = converter.to_bytes(s);


        std::cout << fileStr << std::endl;
        std::cout << currFile << std::endl;
        std::cout << fileStr.size() << std::endl;
        std::cout << std::setlocale(LC_ALL, NULL) << std::endl;
        std::cout << "========================================================================================" << std::endl;
        // Process...
    }
    return;
}

正如您在代码中看到的,我尝试过使用globallocale local variables,但无济于事。

另外,在How can I use std::imbue to set the locale for std::wcout? SO 回答中指出:

所以看起来确实有一个底层 C 库机制 应该首先使用 setlocale 启用以允许注入转换 正常工作。

“晦涩”的机制是这里的问题吗?

在处理文件时是否可以在两种语言环境之间交替? 我应该灌输什么(coutifstreamgetline?)以及如何灌输?

有什么建议吗?

PS:为什么和语言环境相关的一切都这么乱? :|

【问题讨论】:

  • "为什么和语言环境相关的一切都这么乱?" Overengineering 最好的。
  • @Eljay 不知何故,这样一个微不足道的任务必须有一个解决方法......
  • 我会看看我是否能让你的代码工作,但说实话我放弃了 C++ 并在 Python 3 中完成了我的“多种编码的多个文本文件”。
  • @Eljay 我可以使用 python 将每个文件转换为 utf8 编码,然后将 c++ 中的文件作为 utf8 处理,但解决问题的方法有点冗长
  • 最简单的方法是忘记 C++ 中曾经存在的语言环境并使用第三方库,例如 libiconv。

标签: c++ character-encoding locale ifstream wifstream


【解决方案1】:

这在我的 Linux 机器上按预期对我有用,但在 Cygwin 下的 Windows 机器上却不适用(两台机器上的可用语言环境集显然相同,但 std::locale::locale 对每个可以想象的语言环境字符串都失败了)。

#include <iostream>
#include <fstream>
#include <locale>
#include <string>

void printFile(const char* name, const char* loc)
{
  try {
    std::wifstream inFile;
    inFile.imbue(std::locale(loc));
    inFile.open(name);
    std::wstring line;
    while (getline(inFile, line))
      std::wcout << line << '\n';
  } catch (std::exception& e) {
    std::cerr << e.what() << std::endl;
  }
}

int main()
{
  std::locale::global(std::locale("en_US.utf8"));

  printFile ("gtext-u8.txt", "de_DE.utf8");       // utf-8 text: grüßen
  printFile ("gtext-legacy.txt", "de_DE@euro");   // iso8859-15 text: grüßen
}

输出:

grüßen
grüßen

【讨论】:

  • 在我的平台上,语言环境是"en_US.UTF-8", "de_DE.UTF-8", "de_DE.ISO8859-15",但你的代码对我有用。
  • @n.m.谢谢你的回答。阅读The C++ programming language 中的语言环境以及您的示例让我意识到如何处理语言环境。使用函数来处理范围也很好。现在我明白为什么有些人说std::locale::global(); 真的很糟糕。它可能只对std::cout有用。
  • @Nik-Lz 按钮位于页面顶部附近。
  • 我必须有一个奇怪的设置,因为我这个程序编译但没有给出任何输出。据我所见,std::locale::global(std::locale("en_US.utf8")); 之后没有输出
  • @albert 是的,Windows 很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多