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 插入字符串中,但它只是保存了错误字符。我可以更改wstrings
和wchat_t
的所有类型,但是根据http://www.cplusplus.com/reference/locale/,语言环境设置显然已经这样做了
在 C++ 中,语言环境由语言环境类的对象表示。每个 这些语言环境对象中包含使用 一组文化相关的特征。
我正在 Cygwin 上的 gcc
6.4.0 版本上编译。我也知道我可以使用gdb
进行调试,但在这个阶段它并没有多大帮助。
【问题讨论】:
您看到的字符可能是多字节编码中的前导字节,可能是 UTF-8。孤立起来是没有意义的,只有结合多字节序列的其余部分才有意义。 但是既然我已经设置了语言环境,字符不应该总是多字节吗? 我认为从基于char
的流中读取char
s 时,语言环境不会产生任何影响。它只是按原样为您提供流的字节。如果您使用宽流,答案可能会改变,例如wifstream
。另外,我想知道 setlocale
call 是否成功 - "portuguese-brazilian"
在我看来不是一个有效的语言环境名称。检查函数的返回值。
是的,对"portuguese-brazilian"
的调用返回了null
。我会在问题中纠正它。现在printf ("Locale is: %s\n", setlocale(LC_ALL,"pt_BR.UTF-8") );
打印出我想要的语言环境。更改为 wifstream
但似乎不起作用。它给出了以下错误:`从“std::basic_istreamchar 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();
简而言之,对代码的调整是:
将所有char
、string
和iostream更改为多字节格式(例如:wchar_t
、wstring
和wcout
);
在每个字符或字符串(L''
或L""
)之前添加L
以进行多字节转换。
【讨论】:
以上是关于c++ 为啥 ifstream get() 函数返回无法识别的特殊字符?的主要内容,如果未能解决你的问题,请参考以下文章
在 C++/CLI 中使用 std::basic_ifstream::get() 时出现 AccessViolationException,为啥?
ifstream 函数“get”如何改变它的字符参数? [复制]
在 C++ 中用于在函数中使用数组,其中使用 ifstream 将数据从 .txt 文件输入到数组中
c++ 从具有相同循环的 ifstream 或 stringstream 中读取