C++ tellg() 不适用于 getline()?

C++ tellg() doesn't work with getline()?

本文关键字:getline 适用于 不适用 C++ tellg      更新时间:2023-10-16

我知道这个标题听起来很疯狂,但我现在亲身经历过,我想不出为什么会失败。

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

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

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

我使用的代码非常简单,基本上是

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

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

编辑:附加信息

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

第2版:根据此线程:http://www.cplusplus.com/forum/beginner/3599/#msg15540由于某种gcc错误,不可能将tellg与getline一起使用。事实真的是这样吗?(你在互联网上读到的并不总是真的=P)

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

哨兵的构造首先检查任何错误标志(如eofbit),如果设置了,则设置failbit并返回。详见此处(C++03 27.6.1.1.2、C++11 27.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

顺便说一句,你所指的bug可能就是这个(这有点不清楚,因为你链接的帖子很遗憾地缺少了任何细节——如果发帖者愿意支持他声称这是一个已知的bug,例如链接,那就更好了)。

如果是这样的话,你应该注意的第一件事是,它在十多年前就被修复了,所以,除非你使用的是一个绝对古老的gcc,否则它现在不会成为问题。

std::istream::tellg在设置流的错误标志时不会告诉您任何信息。根据其规格,

返回:构造哨兵对象后,如果是fail() != false,则返回pos_type(-1)表示失败。否则,返回rdbuf()->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;