gpt4 book ai didi

c++ - ifstream::unget() 失败。 MS 的实现有问题还是我的代码有误?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:27:03 30 4
gpt4 key购买 nike

昨天我在相当简单的代码中发现了一个奇怪的错误,该错误基本上是从 ifstream 获取文本并将其标记化。实际上失败的代码进行了多次 get()/peek() 调用以查找 token “/*”。如果在流中找到 token ,则调用 unget() 以便下一个方法看到以 token 开头的流。

有时,似乎只取决于文件的长度,unget() 调用会失败。它在内部调用 pbackfail() 然后返回 EOF。然而,在清除流状态后,我可以愉快地读取更多字符,所以它不完全是 EOF..

深入研究后,这里是可以轻松重现问题的完整代码:

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

//generate simplest string possible that triggers problem
void GenerateTestString( std::string& s, const size_t nSpacesToInsert )
{
s.clear();
for( size_t i = 0 ; i < nSpacesToInsert ; ++i )
s += " ";
s += "/*";
}

//write string to file, then open same file again in ifs
bool WriteTestFileThenOpenIt( const char* sFile, const std::string& s, std::ifstream& ifs )
{
{
std::ofstream ofs( sFile );
if( ( ofs << s ).fail() )
return false;
}
ifs.open( sFile );
return ifs.good();
}

//find token, unget if found, report error, show extra data can be read even after error
bool Run( std::istream& ifs )
{
bool bSuccess = true;

for( ; ; )
{
int x = ifs.get();
if( ifs.fail() )
break;
if( x == '/' )
{
x = ifs.peek();
if( x == '*' )
{
ifs.unget();
if( ifs.fail() )
{
std::cout << "oops.. unget() failed" << std::endl;
bSuccess = false;
}
else
{
x = ifs.get();
}
}
}
}

if( !bSuccess )
{
ifs.clear();
std::string sNext;
ifs >> sNext;
if( !sNext.empty() )
std::cout << "remaining data after unget: '" << sNext << "'" << std::endl;
}

return bSuccess;
}

int main()
{
std::string s;
const char* testFile = "tmp.txt";
for( size_t i = 0 ; i < 12290 ; ++i )
{
GenerateTestString( s, i );

std::ifstream ifs;
if( !WriteTestFileThenOpenIt( testFile, s, ifs ) )
{
std::cout << "file I/O error, aborting..";
break;
}

if( !Run( ifs ) )
std::cout << "** failed for string length = " << s.length() << std::endl;
}
return 0;
}

当字符串长度接近典型的 multiple=of-2 缓冲区大小 4096、8192、12288 时,程序失败,输出如下:

oops.. unget() failed
remaining data after unget: '*'
** failed for string length = 4097
oops.. unget() failed
remaining data after unget: '*'
** failed for string length = 8193
oops.. unget() failed
remaining data after unget: '*'
** failed for string length = 12289

在 Windows XP 和 7 上测试时会发生这种情况,两者均在调试/ Release模式下编译,动态/静态运行时,32 位和 64 位系统/编译,全部使用 VS2008,默认编译器/链接器选项。在64位Debian系统上用gcc4.4.5测试没发现问题。

问题:

  1. 其他人可以测试一下吗?我非常感谢 SO 的积极合作。
  2. 代码中是否有不正确的地方会引起问题(不说是否合理)
  3. 或任何可能触发此行为的编译器标志?
  4. 所有解析器代码对于应用程序来说都相当关键,并且经过了大量测试,但当然在测试代码中没有发现这个问题。我应该想出极端的测试用例吗?如果是的话,我该怎么做?我怎么能预测这会导致问题?
  5. 如果这确实是一个错误,我应该在哪里最好地报告它?

最佳答案

is there anything that is not correct in the code that could cause the problem (not talking about whether it makes sense)

是的。标准流必须至少有 1 unget()位置。所以你可以安全地只做一个 unget()在调用 get() 后.当您调用 peek()并且输入缓冲区为空,underflow()发生并且实现清除缓冲区并加载新的数据部分。注意 peek()不会增加当前输入位置,因此它指向缓冲区的开头。当您尝试 unget()该实现试图减少当前输入位置,但它已经在缓冲区的开头,因此它失败了。

当然这取决于实现。如果流缓冲区包含多个字符,则它有时会失败,有时不会。据我所知,微软的实现只在 basic_filebuf 中存储一个字符(除非你明确指定一个更大的缓冲区)并且依赖于 <cstdio>内部缓冲(顺便说一句,这就是 MVS iostream 速度慢的原因之一)。 unget() 时,质量实现可能会再次从文件加载缓冲区失败。但这不是必需的。

尝试修复您的代码,这样您就不需要超过一个 unget()位置。如果你真的需要它,那么用保证 unget() 不会失败的流来包装流(看看 Boost.Iostreams)。另外,您发布的代码是无稽之谈。它试图 unget()然后 get()再次。为什么?

关于c++ - ifstream::unget() 失败。 MS 的实现有问题还是我的代码有误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3820396/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com