【问题标题】:C++ tellg() doesn't work with getline()?C++ tellg() 不适用于 getline()?
【发布时间】:2013-06-23 00:06:04
【问题描述】:

我知道这个标题听起来很疯狂,但我现在正在亲身体验,我想不出任何失败的原因。

我正在使用 getline() 读取文件

在阅读结束时,我调用了tellg()。但是,此调用总是失败(返回值为 -1)。

tellg() 不能与 getline() 一起工作是已知问题还是我做错了什么?

我用的代码很简单,基本上

while(getline(file,line))
{
//tokenize and do other things
}
cout<<file.tellg()<<endl;

有问题的文件是普通磁盘上的一个简单的 txt 文件,我尝试了一个带和不带 CRLF 的文件,没有区别。

编辑:附加信息

gcc/g++ 4.1.2,Linux (RHEL 5)

编辑2: 根据这个线程: http://www.cplusplus.com/forum/beginner/3599/#msg15540 由于某种 gcc 错误,无法将 tellg 与 getline 一起使用。真的是这样吗? (你在网上看到的不一定都是真的=P)

【问题讨论】:

  • file.tellg() 之前使用file.clear() 清除failbit
  • cplusplus.com 不是可靠来源。它的初学者论坛更是如此。

标签: c++ ifstream getline


【解决方案1】:

tellg() 函数的工作原理是尝试构造一个哨兵对象,然后在返回正确值之前检查failbit。如果设置了failbit,则返回-1。可以在here 找到详细信息,或者,如果您更喜欢更官方的来源并且不介意干巴巴的阅读,ISO C++ 标准(27.6.1.3/36a-37 用于 C++03,27.7.2.3/39-40 用于 C++11)。

哨兵的构造首先检查任何error flags(如eofbit),如果设置,则设置failbit并返回。详见here(C++0327.6.1.1.2,C++1127.7.2.1.3),

因此,设置文件结束标志后的tellg() 将失败。在getline 返回 false 之前,您一直在读取行,这意味着正在设置流的eofbit,因此您已到达文件末尾。

您可以使用以下程序查看行为:

#include <iostream>
#include <iomanip>

int main (void) {
    std::string line;
    while (std::getline (std::cin, line)) {
        if (line.length() > 20)
            line = line.substr(0,17) + "...";
        std::cout << "tellg() returned "
            << std::setw(5) << std::cin.tellg()
            << " after " << line << "\n";
    }
    //std::cin.clear();
    std::cout << "tellg() returns: "
        << std::cin.tellg() << '\n';
    return 0;
}

当您运行它并将文件本身作为输入提供时,您会看到:

tellg() returned    20 after #include <iostream>
tellg() returned    39 after #include <iomanip>
tellg() returned    40 after 
tellg() returned    58 after int main (void) {
tellg() returned    80 after     std::string l...
tellg() returned   124 after     while (std::g...
tellg() returned   156 after         if (line....
tellg() returned   202 after             line ...
tellg() returned   243 after         std::cout...
tellg() returned   291 after             << st...
tellg() returned   333 after             << " ...
tellg() returned   339 after     }
tellg() returned   363 after     //std::cin.cl...
tellg() returned   400 after     std::cout << ...
tellg() returned   437 after         << std::c...
tellg() returned   451 after     return 0;
tellg() returned   453 after }
tellg() returned   454 after 
tellg() returns: -1

如果您取消注释该代码中清除错误状态变量的行,它将起作用:

tellg() returned    20 after #include <iostream>
tellg() returned    39 after #include <iomanip>
tellg() returned    40 after 
tellg() returned    58 after int main (void) {
tellg() returned    80 after     std::string l...
tellg() returned   124 after     while (std::g...
tellg() returned   156 after         if (line....
tellg() returned   202 after             line ...
tellg() returned   243 after         std::cout...
tellg() returned   291 after             << st...
tellg() returned   333 after             << " ...
tellg() returned   339 after     }
tellg() returned   361 after     std::cin.clea...
tellg() returned   398 after     std::cout << ...
tellg() returned   435 after         << std::c...
tellg() returned   449 after     return 0;
tellg() returned   451 after }
tellg() returned   452 after 
tellg() returns: 452

而且,顺便说一句,看起来您所指的错误可能是this one(这有点不清楚,因为您链接到的帖子遗憾地缺少任何细节 - 如果发帖者打扰会更好支持他关于这是一个已知错误的断言,例如通过链接到它)。

如果是这样的话,首先你应该注意到它是在十多年前修复的,除非你使用的是绝对古代 @987654340 @,现在不会有问题了。

【讨论】:

  • 在调用 tellg() 之前,我实际上并没有在输入流上调用 close()。您是说 getline() 在运行到文件末尾后设置故障位?
  • 更具体地说,getline 将在不提取字符时设置failbit,这在文件末尾很常见。
  • 即使getline没有设置failbit,它也会在文件末尾设置eofbit,因此哨兵构造会设置failbit。
  • 在调用其缓冲区的close 函数或析构函数之前,该流不会关闭。要让tellg 再次工作,只需调用clear
  • @Potatoswatter,正确,我已经调整了答案,说设置了 eof 位而不是关闭文件,并添加了解决方案。我也确认了 - 当循环退出时,eofbit 和 failbit 都设置了,badbit 没有。
【解决方案2】:

std::istream::tellg 不会告诉您是否设置了流的错误标志。根据其规范,

返回:构造一个哨兵对象后,如果fail() != false,返回pos_type(-1)表示失败。否则,返回rdbuf()-&gt;pubseekoff(0, cur, in)

引用std::istream::sentry,如果eof 已设置,则设置fail

但是faileofclear 函数清除,这就是你需要做的所有事情。

while(getline(file,line))
{
//tokenize and do other things
}
file.clear(); // reset error state
cout<<file.tellg()<<endl;

即使您不理会clearpubseekoff 功能仍然有效,所以它也有效:

cout<< static_cast< std::streamoff >( file.rdbuf()->pubseekoff(0, std::ios::cur, std::ios::in) )
    <<endl;

【讨论】:

  • @paxdiablo 谢谢,我今天成了僵尸。
猜你喜欢
  • 2012-01-10
  • 1970-01-01
  • 2021-04-07
  • 2013-03-21
  • 2023-02-04
  • 2012-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多