c++ tellg()对getline()不起作用?

时间:2022-09-01 17:10:01

I know the title sounds crazy, but I'm experiencing this firsthand right now and I can't think of any reason why this is failing.

我知道这个名字听起来很疯狂,但我现在正亲身经历这一切,我想不出有什么理由让它失败。

I am reading through a file using getline()

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

At the end of the reading, I call tellg(). However, this call always fails (return value of -1).

在阅读的最后,我叫tellg()。但是,这个调用总是失败(返回-1)。

Is it a known issue that tellg() doesn't work with getline() or am I doing something else wrong?

tellg()对getline()不起作用是一个已知的问题,还是我做错了别的事情?

The code I am using is very simple, basically

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

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

The file in question is a simple txt file on a regular disk, I tried a file with and without CRLF and it makes no difference.

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

EDIT: Additional information

编辑:附加信息

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

Linux (RHEL 5)

EDIT2: According to this thread: http://www.cplusplus.com/forum/beginner/3599/#msg15540 It is impossible to use tellg with getline due to some sort of gcc bug. Is this actually the case? (what you read on the internet is not always true =P)

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

2 个解决方案

#1


6  

The tellg() function works by attempting to construct a sentry object and then checking for the failbit before returning a proper value. If the failbit is set, it returns -1. Details can be found here or, if you prefer a more official source and don't mind a dry read, the ISO C++ standard (27.6.1.3/36a-37 for C++03, 27.7.2.3/39-40 for C++11).

tellg()函数的工作方式是尝试构造一个哨兵对象,然后在返回适当的值之前检查故障位。如果设置了failbit,则返回-1。详细信息可以在这里找到,或者,如果您喜欢更正式的来源,而不介意干巴巴地阅读,ISO c++标准(c++ 03, 27.6.3 /36a-37, c++ 03, 27.7.3 /39-40)。

The construction of the sentry first checks any of the error flags (like eofbit) and, if set, it sets the failbit and returns. See here for detail (C++03 27.6.1.1.2, C++11 27.7.2.1.3),

哨兵的构造首先检查任何错误标志(如eofbit),如果设置,则设置故障比特并返回。详见此处(c++ 03 27.6.1.1.2, c++ 11 27.7.2.1.3),

Hence a tellg() after the end of file flag has been set will fail. The fact that you're reading lines until getline returns false means that the stream's eofbit is being set, hence you've reached the end of the file.

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

You can see the behavior with this following program:

您可以通过以下程序看到行为:

#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;
}

When you run that and provide the file itself as input, you see:

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

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

If you uncomment the line in that code which clears the error state variables, it will work:

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

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

And, as an aside, it looks like the bug you're referring to may be this one (it's a little unclear since the post you linked to is sadly missing any detail - it would have been better had the poster bothered to support his assertion that it was a known bug by, for example, linking to it).

说句题外话,它看起来像你指的错误可能是这个(有点不清楚因为后你与令人遗憾错过任何细节,它将会更好的海报去支持他的说法,这是一个已知的缺陷,例如,链接到它)。

If that's the case, the first thing you should notice is that it was fixed more than a decade ago so, unless you're using an absolutely ancient gcc, it's not going to be an issue now.

如果是这样的话,你首先要注意的是,它是十多年前修复的,所以,除非你使用的是一个绝对古老的gcc,否则它现在不会成为问题。

#2


0  

std::istream::tellg does not tell you anything if the stream's error flag is set. According to its spec,

如果设置了流的错误标志,tellg不会告诉您任何信息。

Returns: After constructing a sentry object, if fail() != false, returns pos_type(-1) to indicate failure. Otherwise, returns rdbuf()->pubseekoff(0, cur, in).

返回:在构造一个哨兵对象之后,如果fail() != false,返回pos_type(-1)以指示失败。否则,返回rdbuf()->pubseekoff(0, cur, in)。

Referring to std::istream::sentry, it sets fail if eof is already set.

引用std::istream: sentry,如果eof已经设置,则设置失败。

But fail and eof are cleared by the clear function, which is all you need to do.

但是fail和eof是通过clear函数清除的,这就是您需要做的。

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

And the pubseekoff function still works even if you don't bother with clear, so this works too:

pubseekoff函数仍然可以工作即使你不需要清除,所以这个也可以:

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

#1


6  

The tellg() function works by attempting to construct a sentry object and then checking for the failbit before returning a proper value. If the failbit is set, it returns -1. Details can be found here or, if you prefer a more official source and don't mind a dry read, the ISO C++ standard (27.6.1.3/36a-37 for C++03, 27.7.2.3/39-40 for C++11).

tellg()函数的工作方式是尝试构造一个哨兵对象,然后在返回适当的值之前检查故障位。如果设置了failbit,则返回-1。详细信息可以在这里找到,或者,如果您喜欢更正式的来源,而不介意干巴巴地阅读,ISO c++标准(c++ 03, 27.6.3 /36a-37, c++ 03, 27.7.3 /39-40)。

The construction of the sentry first checks any of the error flags (like eofbit) and, if set, it sets the failbit and returns. See here for detail (C++03 27.6.1.1.2, C++11 27.7.2.1.3),

哨兵的构造首先检查任何错误标志(如eofbit),如果设置,则设置故障比特并返回。详见此处(c++ 03 27.6.1.1.2, c++ 11 27.7.2.1.3),

Hence a tellg() after the end of file flag has been set will fail. The fact that you're reading lines until getline returns false means that the stream's eofbit is being set, hence you've reached the end of the file.

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

You can see the behavior with this following program:

您可以通过以下程序看到行为:

#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;
}

When you run that and provide the file itself as input, you see:

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

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

If you uncomment the line in that code which clears the error state variables, it will work:

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

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

And, as an aside, it looks like the bug you're referring to may be this one (it's a little unclear since the post you linked to is sadly missing any detail - it would have been better had the poster bothered to support his assertion that it was a known bug by, for example, linking to it).

说句题外话,它看起来像你指的错误可能是这个(有点不清楚因为后你与令人遗憾错过任何细节,它将会更好的海报去支持他的说法,这是一个已知的缺陷,例如,链接到它)。

If that's the case, the first thing you should notice is that it was fixed more than a decade ago so, unless you're using an absolutely ancient gcc, it's not going to be an issue now.

如果是这样的话,你首先要注意的是,它是十多年前修复的,所以,除非你使用的是一个绝对古老的gcc,否则它现在不会成为问题。

#2


0  

std::istream::tellg does not tell you anything if the stream's error flag is set. According to its spec,

如果设置了流的错误标志,tellg不会告诉您任何信息。

Returns: After constructing a sentry object, if fail() != false, returns pos_type(-1) to indicate failure. Otherwise, returns rdbuf()->pubseekoff(0, cur, in).

返回:在构造一个哨兵对象之后,如果fail() != false,返回pos_type(-1)以指示失败。否则,返回rdbuf()->pubseekoff(0, cur, in)。

Referring to std::istream::sentry, it sets fail if eof is already set.

引用std::istream: sentry,如果eof已经设置,则设置失败。

But fail and eof are cleared by the clear function, which is all you need to do.

但是fail和eof是通过clear函数清除的,这就是您需要做的。

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

And the pubseekoff function still works even if you don't bother with clear, so this works too:

pubseekoff函数仍然可以工作即使你不需要清除,所以这个也可以:

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