在C ++中使用std :: ifstream读取ASCII文本文件

时间:2017-03-26 08:54:42

标签: c++ character-encoding ifstream

我有一个阿拉伯文件(ASCII),其中包含: 121101الزبونكمال 121102الزبونسعيد 121103الزبونعمار

我想在C ++中使用std :: ifstream读取此文件:

std::ifstream ifs(file.GetFileName());
std::string content((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());

当我使用VS IDE观看内容变量时,出现了字符编码错误: 121101ÇÃÒÈæäßãÇá 121102ÇáÒÈæäÓÚíÏ 121103ÇáÒÈæäÚãÇÑ

我还有托盘和std :: wifstream:

std::wifstream ifs2(file.GetFileName());
std::string content2((std::istreambuf_iterator<wchar_t>(ifs2)), std::istreambuf_iterator<wchar_t>());

我有同样的错误。 有人能帮助我吗? 感谢。

2 个答案:

答案 0 :(得分:2)

  

我有一个阿拉伯语文件(ASCII),其中包含:121101الزبونكمال   121102الزبونسعيد121103الزبونعمار

经过一番澄清后,OP希望:

  

编写读取uft8和ANSI文件的通用函数

为了能够以相同的方式处理内容,我建议转换为UTF-16编码的std::wstring。 OP似乎是针对Windows平台开发的,其中UTF-16是大多数API所期望的编码。在其他平台(Linux)上,将所有内容转换为UTF-8可能更合适。

将ANSI文本文件读入UTF-16编码的wstring

为了能够解码ANSI(又名扩展ASCII),我们必须知道文件的codepage

可以通过流的imbue()方法定义代码页(或更准确地说是语言环境)。在您的情况下,代码页为1256

以下示例读取使用ANSI代码页1256编码的文本文件的内容,并使用需要UTF-16编码字符串的MessageBoxW()显示文本:

#include <fstream>
#include <string>
#include <codecvt>
#include <Windows.h>

int main()
{
    // Use wifstream because we want to read content into a wstring.
    std::wifstream f{"test.txt"};

    // Define the code page of the text file (1256 = Arabic)
    f.imbue( std::locale( ".1256" ) );

    // Read the whole file into a wstring.
    // The stream converts from ANSI to UTF-16 encoding.
    std::wstring s{ std::istreambuf_iterator<wchar_t>( f ), std::istreambuf_iterator<wchar_t>() };

    // Display the string which is now UTF-16 encoded.    
    ::MessageBoxW( NULL, s.c_str(), L"test", 0 );

    return 0;
}

注意std::locale参数是特定于平台的。 “.1256”适用于Windows平台,但这可能不适用于Linux。

将UTF-8编码的文本文件读入UTF-16编码的wstring

为此,我们可以使用std::codecvt_utf8_utf16方面。 使用以下代码替换上一个示例的imbue()调用:

    f.imbue( std::locale( f.getloc(), 
        new std::codecvt_utf8_utf16< wchar_t, 1114111UL, std::consume_header> ) );

标记std::consume_header会跳过byte order mark(如果存在)。

备注:

  • 代码示例已在Windows 10下使用VS2017进行了德语本地化测试。
  • 为简洁起见,我省略了错误处理。打开后和从流中读取后应检查流状态。

创建通用解决方案

上面提供的代码示例要求您事先知道文本文件的编码。以真正通用的方式检测文本文件的编码是一项艰巨的任务,因为没有标准的方法可以做到这一点。它不能可靠地完成,你必须使用一些启发式方法。

如果你可以对你必须处理的文件做一些假设,你可以写一个简单的检测功能。假设文件只属于以下类别:

  • ANSI编码文件,代码页为1256
  • 带有BOM(byte order mark
  • 的UTF-8编码文件

然后,您可以使用std::ifstream读取文件的前3个字节,并将其与{0xEF, 0xBB, 0xBF}进行比较。如果相等,则可以相对确定该文件是UTF-8编码的,因为非UTF-8编码的文件不太可能以这些字节开头。如果不相等,您将假设代码页1256。

答案 1 :(得分:-3)

为什么不使用FILE *?例如,这是我的代码的摘录,我正在为我的游戏阅读save.ini,其中包含不同的保存游戏条目。我喜欢fopen()的是你实际上可以知道文件的格式(如UTF-8,UTF-16等)。

FILE* pFini = fopen ("save\\save.ini", "rt,ccs=UTF-8");
int iLine = 0;

if (pFini == NULL)
{
    cout << "WARNING: cannot open save.ini file." << endl;
    return;
}

while (!feof (pFini))
{
    fgetws (wSaveGames [iLine], 125, pFini);
    iLine++;
    if (iLine >= MAX_SAVEGAME_NUMBER)
        break;
}
fclose (pFini);