Пример того, почему stream:: good is Wrong?

Я дал ответ, который я хотел бы проверить действительность потока каждый раз через цикл здесь.

Мой оригинальный код использовал good и выглядел примерно так:

ifstream foo("foo.txt");

while (foo.good()){
    string bar;
    getline(foo, bar);
    cout << bar << endl;
}

Я тут же указал и сказал, чтобы никогда не тестировать good. Очевидно, это то, что я не понял, но я хочу правильно выполнять ввод/вывод файлов.

Я проверил код с несколькими примерами и не смог сделать код good -testing.

Сначала (это правильно напечатано, заканчивается новой строкой):

bleck 1
blee 1 2
бла
заканчивается в новой строке

Второе (это правильно напечатано, заканчивается последней строкой):

bleck 1
blee 1 2
blah
это не заканчивается новой строкой

Третий был пустой файл (это правильно напечатано, одна новая строка.)

Четвертый - отсутствующий файл (это правильно ничего не печатало.)

Может ли кто-нибудь помочь мне с примером, который демонстрирует, почему good -testing не нужно делать?

Ответ 1

Они ошибались. Мантра "никогда не тестирует .eof()".

Даже эта мантра за бортом, потому что обе полезны для диагностики состояния потока после неудачного извлечения.

Итак, мантра должна быть больше похожа на

Не используйте good() или eof(), чтобы обнаружить eof перед тем, как продолжить читать

То же самое для fail() и bad()

Конечно, stream.good можно с пользой использовать перед использованием потока (например, если поток представляет собой фильтр, который не был успешно открыт)

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


Канонический пример того, почему вы не должны использовать этот метод:

std::istringstream stream("a");
char ch;
if (stream >> ch) {
   std::cout << "At eof? " << std::boolalpha << stream.eof() << "\n";
   std::cout << "good? " << std::boolalpha << stream.good() << "\n";
}

Печать

false
true

Смотрите Live On Coliru

Ответ 2

Это уже рассмотрено в других ответах, но я кратко рассмотрю его для полноты. Единственное функциональное отличие от

while(foo.good()) { // effectively same as while(foo) {
    getline(foo, bar);
    consume(bar); // consume() represents any operation that uses bar
}

и

while(getline(foo, bar)){
    consume(bar);
}

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

Как говорится, мантра за борт. Это упрощение. В чем дело, так это то, что вы не должны consume() результат чтения потока до того, как вы проверите на предмет сбоя или, по крайней мере, EOF (и любой тест перед чтением не имеет значения). Это то, что люди легко делают, когда тестируют good() в состоянии цикла.

Однако дело в getline() заключается в том, что он проверяет EOF внутренне, для вас и возвращает пустую строку, даже если считывается только EOF. Поэтому первая версия может быть примерно похожа на следующий псевдо-С++:

while(foo.good()) {
    // inside getline
    bar = "";               // Reset bar to empty
    string sentry;
    if(read_until_newline(foo, sentry)) {
        // The streams state is tested implicitly inside getline
        // after the value is read. Good
        bar = sentry        // The read value is used only if it valid.
    // ...                  // Otherwise, bar is empty.
    consume(bar);
}

Надеюсь, это иллюстрирует то, что я пытаюсь сказать. Можно сказать, что есть "правильная" версия цикла чтения внутри getline(). Вот почему правило, по крайней мере, частично удовлетворено использованием readline, даже если внешний контур не соответствует.

Но, для других методов чтения, нарушение правила болит больше. Рассмотрим:

while(foo.good()) {
    int bar;
    foo >> bar;
    consume(bar);
}

Вы не всегда получаете дополнительную итерацию, bar в том, что итерация неинициализирована!

Итак, короче говоря, while(foo.good()) в порядке, так как getline() в отличие от некоторых других функций чтения оставляет вывод в допустимом состоянии после чтения бит EOF. и потому, что вам все равно или даже ожидать дополнительной итерации, когда файл пуст.

Ответ 3

оба good() и eof() будут давать вам дополнительную строку в вашем коде. Если у вас есть пустой файл и запустите его:

std::ifstream foo1("foo1.txt");
std::string line;
int lineNum = 1;

std::cout << "foo1.txt Controlled With good():\n";
while (foo1.good())
{
    std::getline(foo1, line);
    std::cout << lineNum++ << line << std::endl;
}
foo1.close();
foo1.open("foo1.txt");
lineNum = 1;

std::cout << "\n\nfoo1.txt Controlled With getline():\n";
while (std::getline(foo1, line))
{
    std::cout << line << std::endl;
}

Вы получите результат

foo1.txt Controlled With good():
1

foo1.txt Controlled With getline():

Это доказывает, что он работает некорректно, поскольку пустой файл никогда не должен читаться. Единственный способ узнать, что использовать условие чтения, поскольку поток всегда будет хорошим при первом чтении.

Ответ 4

Использование foo.good() просто говорит вам, что предыдущая операция чтения работала очень хорошо, и что следующий тоже мог бы работать. .good() проверяет состояние потока в заданной точке. Он не проверяет, достигнут ли конец файла. Допустим, что что-то произошло во время чтения файла (ошибка сети, ошибка os,...). Это не означает, что конец файла был достигнут. Тем не менее .good() терпит неудачу, когда конец файла достигнут, потому что поток больше не может читать.

С другой стороны, .eof() проверяет, действительно ли достигнут конец файла.

Таким образом, .good() может завершиться неудачно, пока конец файла не был достигнут.

Надеюсь, это поможет вам понять, почему использование .good() для проверки конца файла - это плохая привычка.

Ответ 5

Позвольте мне четко сказать, что ответ на вопрос является правильным.

Но вариант, предложенный Натан Оливер, Нейл Кирк и user2079303 использовать readline как условие цикла, а не good. Нужно обращаться к потомству.

Мы сравним цикл в вопросе с следующим циклом:

string bar;

while (getline(foo, bar)){
    cout << bar << endl;
}

Потому что getline возвращает istream, переданный как первый аргумент, и потому что когда istream передается в bool он возвращает !(fail() || bad()), а так как чтение символа EOF будет устанавливать как failbit, так и eofbit, это делает getline допустимым условием цикла.

Однако поведение при использовании getline изменяется как условие, потому что, если строка, содержащая только символ EOF, считывается, цикл выйдет из строя, чтобы исключить вывод этой строки. Это не встречается в примерах 2 и 4. Но пример 1:

bleck 1
blee 1 2
бла
заканчивается в новой строке

Печатает это с условием цикла good:

bleck 1
blee 1 2
бла
заканчивается в новой строке

Но прерывает последнюю строку с условием цикла getline:

bleck 1
blee 1 2
бла
заканчивается в новой строке

Пример 3 - пустой файл:

Печатает это с условием good:

Не печатает ничего с условием getline.

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