读取包含中文字符的文件(C++)

Reading a file that contains chinese characters (C++)

我在读取包含中文字符的文件时遇到问题。我知道文件的编码是Big5.

这是我的示例文件(test.txt),由于中文字符,我无法将其包含在这里:https://gist.github.com/haruka98/974ca2c034ebd8fe7eeac4124739fc41

这是我的最小代码示例 (main.cpp),我实际使用的代码会分解每一行并处理不同的字段。

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

int main(int argc, char* argv[]) {
    setlocale(LC_ALL, "Chinese-traditional");
    std::wstring wstr;
    std::wifstream input_file("test.txt");
    std::wofstream output_file("test_output.txt");
    int counter = 0;
    while(std::getline(input_file, wstr)) {
        for(int i = 0; i < wstr.size(); i++) {
            if(wstr[i] == L'|') {
                counter++;
            }
        }
        output_file << wstr << std::endl;
    }
    input_file.close();
    output_file.close();
    std::cout << counter << std::endl;
    return 0;
}

编译我的程序:

g++ -o test main.cpp -std=c++17

在 Windows 10 我得到了预期的输出。我将整个文件复制到“test_output.txt”和终端中的 129 输出。

在 Linux (Debian 9) 上,我得到终端输出 4,文件“test_output.txt”仅包含第一行和“1|”从第二个开始。

这是我尝试过的:

我的第一个猜测是同时使用 Windows 和 Linux 时的 CR LF 和 LF 问题。但是用文件测试 CR LF 和 LF 都没有帮助。

然后我想到“繁体中文”可能在Linux上不起作用。我用“zh_TW.BIG5”替换了它,但也没有得到预期的结果。

setlocale 影响程序的语言环境。

它对终端显示的文本的默认编码没有影响window。终端 window 是一个独立的应用程序,有自己的语言环境。

几乎所有现代 Linux 发行版都默认使用 UTF-8 作为系统控制台和终端的编码 windows(gnome-terminal、Konsole、xfce4-terminal 等。 ..).

更改程序的区域设置只会影响应用程序解释文本的方式,但终端仍希望您的应用程序生成 UTF-8 输出。终端 window 不知道终端 window 中应用程序 运行ning 的内部区域设置。终端 windows 期望应用程序使用系统区域设置的字符编码生成输出。

理论上,C 库可以知道默认的系统编码并默默地转码所有输出,但这种方式行不通。

您必须在 Linux 上使用 iconv 库完成将 big5 转码为 UTF-8 的所有工作。

一个低成本、廉价的快捷方式,是让您的程序分叉 运行 iconv 命令行工具作为子进程,并将其输出通过管道传递给它,然后让 iconv 执行即时转码。

首先检查您是否安装了“Chinese-traditional”的语言环境。 Linux 这是 zh_TW.UTF-8。您可以使用 locale -a 检查。如果未列出,请安装它:

sudo locale-gen zh_TW.UTF-8
sudo update-locale

(有一个语言环境列表 here,其名称在 Linux 和 Windows 上。)

然后对输入和输出流使用 imbue 来设置流的语言环境。

默认情况下,std::wcout 与底层 stdout C 流同步,后者使用 ASCII 映射并显示 ?代替它无法处理的 Unicode 字符。如果要将 Unicode 字符打印到终端,则必须关闭该同步。您可以用一行来完成并设置终端的区域设置:

std::ios_base::sync_with_stdio(false);
std::wcout.imbue(loc);

您的代码的修改版本:

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

int main(int argc, char* argv[])
{
    auto loc = std::locale("zh_TW.utf8");

    //Disable synchronisation with stdio & set locale
    std::ios::sync_with_stdio(false);
    std::wcout.imbue(loc);

    //Set locale of input stream
    std::wstring wstr;
    std::wifstream input_file("test.txt");
    input_file.imbue(loc);

    //Set locale of outputput stream
    std::wofstream output_file("test_output.txt");
    output_file.imbue(loc);

    int counter = 0;
    while(std::getline(input_file, wstr)) {
        for(int i = 0; i < wstr.size(); i++) {
            if(wstr[i] == L'|') {
                counter++;
            }
        }
        std::wcout << wstr << std::endl;
        output_file << wstr << std::endl;
    }
    input_file.close();
    output_file.close();
    std::wcout << counter << std::endl;
    return 0;
}

使用 std::wcout 打印 std::wstring 而不是 std::cout :-)