c++ 为啥 ifstream get() 函数返回无法识别的特殊字符?

Posted

技术标签:

【中文标题】c++ 为啥 ifstream get() 函数返回无法识别的特殊字符?【英文标题】:c++ Why is ifstream get() function returning unrecognized special characters?c++ 为什么 ifstream get() 函数返回无法识别的特殊字符? 【发布时间】:2018-08-04 18:06:49 【问题描述】:

我正在尝试在 C++ 中实现一个 Text 类,它加载一个文本文件 (.txt),搜索该文件中的每个字符并存储所有单词和所有分隔符(在这种情况下,分隔符将是所有不是一个字符)在两个各自的向量(#include <vector>)中。由于文本文件 main 包含特殊字符,因此我使用 setlocale(LC_ALL, "pt_BR.UTF-8") 为程序设置了语言环境。

当下面的代码执行时(作为类文本的构造函数),我注意到在构造 ifstream 类并且代码进入while 循环后,char c,我使用arch.get(c),包含一个无法识别的字符(这里的这个坏男孩:▒)。

在这种c 是特殊字符的情况下,它将被保存在一个字符串(string d)中,并且在下一个循环中,如果一个字母(由isalpha(c) 标识)出现在文件中,它将string d 存储在相应的分隔符向量上。相同的逻辑适用于字母,因为它们保存在string p 上,然后保存在单词向量中(words = palavras 英文)。我最困惑的部分是当我打印string d 并检查它的值时,文件中识别的特殊字符正确显示。

为什么特殊字符只有插入string 才能识别?为什么arch.get(c) 函数返回一个无法识别的字符?

以下代码是类Text的构造函数。用于测试的印刷品有 cmets 用于指示。

Text::Text( string na ) 
    // Inicialization of variables
    total_size = 0;
    word_first_flag = false;
    namearch = na;
    string p = "";
    string d = "";
    vector<string>::iterator it_delim;
    it_palavras = palavras.begin();
    it_delim = delim.begin();

    setlocale(LC_ALL, "pt_BR.UTF-8");

    ifstream arch(namearch);

    char c;

    while(arch.get(c)) 
        if(total_size > 10000)
            break;

            cout << c << endl; // Prints ▒

        switch (isalpha(c))  // does not recognize special characters
            case 0:
                if(p == "") 
                    d = d + c;
                    cout << "-" << d << "-"<< endl; // Prints correct char
                
                else 
                    Palavra paux;
                    paux = p;
                    palavras.push_back(paux);
                    p = "";
                    d = d + c;
                
            break;
            default:
                if(total_size == 0) word_first_flag = true;

                if(d == "") 
                    p = p + c;
                
                else 
                    delim.push_back(d);
                    cout << "-" << d << "-" << " Inserted!" << endl << endl; // Also prints correct char
                    d = "";
                    p = p + c;
                
            break;
            

            ++total_size;
        
    

    if(d != "")
        delim.push_back(d);

    it_palavras = palavras.begin();
    arch.close();

根据locale 类的文档,对于特殊字符,一切都应该正常工作。但事实并非如此。我还尝试将 c 插入字符串中,但它只是保存了错误字符。我可以更改wstringswchat_t 的所有类型,但是根据http://www.cplusplus.com/reference/locale/,语言环境设置显然已经这样做了

在 C++ 中,语言环境由语言环境类的对象表示。每个 这些语言环境对象中包含使用 一组文化相关的特征。

我正在 Cygwin 上的 gcc 6.4.0 版本上编译。我也知道我可以使用gdb 进行调试,但在这个阶段它并没有多大帮助。

【问题讨论】:

您看到的字符可能是多字节编码中的前导字节,可能是 UTF-8。孤立起来是没有意义的,只有结合多字节序列的其余部分才有意义。 但是既然我已经设置了语言环境,字符不应该总是多字节吗? 我认为从基于char 的流中读取chars 时,语言环境不会产生任何影响。它只是按原样为您提供流的字节。如果您使用宽流,答案可能会改变,例如wifstream。另外,我想知道 setlocalecall 是否成功 - "portuguese-brazilian" 在我看来不是一个有效的语言环境名称。检查函数的返回值。 是的,对"portuguese-brazilian" 的调用返回了null。我会在问题中纠正它。现在printf ("Locale is: %s\n", setlocale(LC_ALL,"pt_BR.UTF-8") ); 打印出我想要的语言环境。更改为 wifstream 但似乎不起作用。它给出了以下错误:`从“std::basic_istream::char_type&aka wchar_t&”类型的非const引用的无效初始化: char_type aka wchar_t` 发生这种情况是因为字符大小错误吗? 你当然还需要把char c;改成wchar_t c; 【参考方案1】:

因此,@Igor Tandetnik 提供了非常有用的见解,并对这些类型的数据进行了一些测试,我已经更改了原始代码,以便它正确地从存档中获取特殊字符。

Text::Text( string na ) 
// Inicialization of variables
total_size = 0;
word_first_flag = false;
namearch = na;
wstring p = L"";
wstring d = L"";
vector<wstring>::iterator it_delim;
it_palavras = palavras.begin();
it_delim = delim.begin();

setlocale(LC_ALL, "pt_BR.UTF-8");

wifstream arch(namearch);

wchar_t c;

while(arch.get(c)) 
    if(total_size > 10000)
        break;

        wcout << c << endl;

    switch (iswalpha(c)) 
        case 0:
            if(p == L"") 
                d = d + c;
                wcout << "-" << d << "-"<< endl;
            
            else 
                Palavra paux;
                paux = p;
                palavras.push_back(paux);
                p = L"";
                d = d + c;
            
        break;
        default:
            if(total_size == 0) word_first_flag = true;

            if(d == L"") 
                p = p + c;
            
            else 
                delim.push_back(d);
                wcout << "-" << d << "-" << " Inserted!" << endl << endl;
                d = L"";
                p = p + c;
            
        break;
        

        ++total_size;
    


if(d != L"")
    delim.push_back(d);

it_palavras = palavras.begin();
arch.close();

简而言之,对代码的调整是:

    将所有charstringiostream更改为多字节格式(例如:wchar_twstringwcout);

    在每个字符或字符串(L''L"")之前添加L 以进行多字节转换。

【讨论】:

以上是关于c++ 为啥 ifstream get() 函数返回无法识别的特殊字符?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++/CLI 中使用 std::basic_ifstream::get() 时出现 AccessViolationException,为啥?

ifstream 函数“get”如何改变它的字符参数? [复制]

在 C++ 中用于在函数中使用数组,其中使用 ifstream 将数据从 .txt 文件输入到数组中

c++ 从具有相同循环的 ifstream 或 stringstream 中读取

C++文件读写详解(ofstream,ifstream,fstream)

C ++为啥在使用类时应该使用get和set函数[重复]