C++ Tellg() не работает с getline()?

Я знаю, что название звучит безумно, но я испытываю это на собственном опыте прямо сейчас и не могу придумать ни одной причины, по которой это не удается.

Я читаю файл, используя getline()

В конце чтения я вызываю tellg(). Однако этот вызов всегда терпит неудачу (возвращаемое значение -1).

Известно ли, что tellg() не работает с getline(), или я делаю что-то еще неправильно?

Код, который я использую, очень прост, в основном

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

Рассматриваемый файл представляет собой простой текстовый файл на обычном диске, я пробовал файл с CRLF и без него, и это не имеет значения.

РЕДАКТИРОВАТЬ: Дополнительная информация

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

EDIT2: Согласно этой теме: http://www.cplusplus.com/forum/beginner/3599/#msg15540 Невозможно использовать tellg с getline из-за какой-то ошибки gcc. Так ли это на самом деле? (то, что вы читаете в интернете, не всегда верно =P)


person user788171    schedule 26.06.2013    source источник
comment
Используйте file.clear() перед file.tellg(), чтобы очистить failbit.   -  person Vaughn Cato    schedule 26.06.2013
comment
cplusplus.com не является надежным источником. Его форумы для начинающих вдвойне хороши.   -  person Potatoswatter    schedule 26.06.2013


Ответы (2)


Функция tellg() работает, пытаясь создать объект часового, а затем проверяя 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

Кроме того, похоже, что ошибка, о которой вы говорите, может быть этим один (это немного неясно, поскольку в сообщении, на которое вы ссылаетесь, к сожалению, отсутствуют какие-либо детали - было бы лучше, если бы автор потрудился поддержать свое утверждение о том, что это известная ошибка, например, ссылка на него).

Если это так, первое замечание, которое вы должны заметить, это то, что оно было исправлено более десяти лет назад, поэтому, если вы не используете абсолютно древний gcc, он не будет быть проблемой сейчас.

person paxdiablo    schedule 26.06.2013
comment
На самом деле я не вызываю close() для входного потока перед вызовом tellg(). Вы говорите, что getline() устанавливает бит ошибки после того, как он дойдет до конца файла? - person user788171; 26.06.2013
comment
В частности, getline установит failbit, когда не будет извлечено никаких символов, что будет типичным в конце файла. - person Vaughn Cato; 26.06.2013
comment
Даже если getline не устанавливает бит отказа, он установит eofbit в конце файла, и из-за этого конструкция часового установит бит отказа. - person paxdiablo; 26.06.2013
comment
Поток не закрывается до тех пор, пока не будет вызвана функция close его буфера или деструктор. Чтобы снова заставить tellg работать, просто вызовите clear. - person Potatoswatter; 26.06.2013
comment
@Potatoswatter, правильно, я изменил ответ, чтобы сказать, что бит eof установлен, а не файл закрыт, и добавил решение. Я также подтвердил - и eofbit, и failbit устанавливаются при выходе из цикла, а badbit - нет. - person paxdiablo; 26.06.2013

std::istream::tellg ничего вам не говорит, если установлен флаг ошибки потока. Согласно его спецификации,

Возвращает: после построения объекта-сторожа, если fail() != false, возвращает pos_type(-1), чтобы указать на ошибку. В противном случае возвращает rdbuf()->pubseekoff(0, cur, in).

Ссылаясь на std::istream::sentry, он устанавливает fail, если eof уже установлено.

Но fail и eof очищаются функцией clear, и это все, что вам нужно сделать.

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

И функция pubseekoff по-прежнему работает, даже если вы не возитесь с clear, так что это тоже работает:

cout<< static_cast< std::streamoff >( file.rdbuf()->pubseekoff(0, std::ios::cur, std::ios::in) )
    <<endl;
person Potatoswatter    schedule 26.06.2013
comment
@paxdiablo Спасибо, сегодня я был зомби. - person Potatoswatter; 26.06.2013