#c #error-handling #istream
#c #обработка ошибок #istream
Вопрос:
Идиоматический цикл для чтения из istream — это
while (thestream >> value)
{
// do something with value
}
Теперь у этого цикла есть одна проблема: он не будет различать, завершился ли цикл из-за конца файла или из-за ошибки. Для примера возьмем следующую тестовую программу:
#include <iostream>
#include <sstream>
void readbools(std::istreamamp; is)
{
bool b;
while (is >> b)
{
std::cout << (b ? "T" : "F");
}
std::cout << " - " << is.good() << is.eof() << is.fail() << is.bad() << "n";
}
void testread(std::string s)
{
std::istringstream is(s);
is >> std::boolalpha;
readbools(is);
}
int main()
{
testread("true false");
testread("true false tr");
}
Первый вызов testread
содержит два допустимых bools и, следовательно, не является ошибкой. Второй вызов заканчивается третьим, неполным bool, и, следовательно, является ошибкой. Тем не менее, поведение обоих одинаково. В первом случае чтение логического значения завершается неудачей, потому что его нет, в то время как во втором случае оно завершается неудачей, потому что оно неполное, и в обоих случаях выполняется ошибка EOF. Действительно, приведенная выше программа дважды выводит одну и ту же строку:
TF - 0110
TF - 0110
Чтобы решить эту проблему, я придумал следующее решение:
while (thestream >> std::ws amp;amp; !thestream.eof() amp;amp; thestream >> value)
{
// do something with value
}
Идея состоит в том, чтобы обнаружить обычный EOF, прежде чем пытаться извлечь значение. Поскольку в конце файла могут быть пробелы (что не было бы ошибкой, но привело бы к тому, что при чтении последнего элемента не попадал EOF), я сначала отбрасываю любые пробелы (которые не могут завершиться ошибкой), а затем проверяю EOF. Только если я не в конце файла, я пытаюсь прочитать значение.
Для моего примера программы это действительно, кажется, работает, и я получаю
TF - 0100
TF - 0110
Итак, в первом случае (правильный ввод) fail()
возвращает false .
Теперь мой вопрос: гарантированно ли это решение сработает, или мне просто (не) повезло, что оно дало желаемый результат? Также: есть ли более простой (или, если мое решение неверно, правильный) способ получить желаемый результат?
Комментарии:
1. каков желаемый результат? также проверить, является ли файл допустимым? в обоих случаях вы получаете одинаковый результат…
2. @neagoegab: желаемый результат — определить, был ли цикл завершен только из-за достижения конца файла или из-за ошибочной записи. И, по крайней мере, в моем эксперименте результаты не совпадают, смотрите третью цифру четырехзначного блока: в случае отсутствия ошибки он читает 0100, в случае ошибки он читает 0110. Поскольку третий бит является значением
fail()
, это означает, что, по крайней мере, для этого теста,fail()
можно различать оба случая.3. Тогда ваш ответ правильный. Также, пожалуйста, обратите внимание, что вы проверяете и обрабатываете поток одновременно. Если для вас это не проблема, то все в порядке…
4. @neagoegab: идиоматический цикл также проверяет и обрабатывает поток одновременно.
5. Я думаю, что идиоматический цикл предполагает, что данные в потоке «действительны»… что действительно означает для примера приложения. Ваша вторая строка не является допустимым вводом.
Ответ №1:
Очень легко отличить EOF от других ошибок, если вы не настроили поток на использование исключений.
Просто проверьте stream.eof()
в конце.
Перед этим проверяйте только наличие сбоя / безотказности, например, stream.fail()
или !stream
. Обратите внимание, что good
это не противоположно fail
. Так что вообще никогда даже не смотрите на good
, только на fail
.
Редактировать:
Некоторый пример кода, а именно ваш пример, изменен, чтобы отличать плохую спецификацию bool в данных:
#include <iostream>
#include <sstream>
#include <string>
#include <stdexcept>
using namespace std;
bool throwX( string constamp; s ) { throw runtime_error( s ); }
bool hopefully( bool v ) { return v; }
bool boolFrom( string constamp; s )
{
istringstream stream( s );
(stream >> boolalpha)
|| throwX( "boolFrom: failed to set boolalpha mode." );
bool resu<
(stream >> result)
|| throwX( "boolFrom: failed to extract 'bool' value." );
char c; stream >> c;
hopefully( stream.eof() )
|| throwX( "boolFrom: found extra characters at end." );
return resu<
}
void readbools( istreamamp; is )
{
string word;
while( is >> word )
{
try
{
bool const b = boolFrom( word );
cout << (b ? "T" : "F") << endl;
}
catch( exception constamp; x )
{
cerr << "!" << x.what() << endl;
}
}
cout << "- " << is.good() << is.eof() << is.fail() << is.bad() << "n";
}
void testread( string constamp; s )
{
istringstream is( s );
readbools( is );
}
int main()
{
cout << string( 60, '-' ) << endl;
testread( "true false" );
cout << string( 60, '-' ) << endl;
testread( "true false tr" );
cout << string( 60, '-' ) << endl;
testread( "true false truex" );
}
Пример результата:
------------------------------------------------------------ T F - 0110 ------------------------------------------------------------ T F !boolFrom: не удалось извлечь значение 'bool'. - 0110 ------------------------------------------------------------ T F !boolFrom: обнаружены дополнительные символы в конце. - 0110
Правка 2: в опубликованном коде и результатах добавлен пример использования eof()
проверки, который я забыл.
Правка 3: В следующем соответствующем примере используется предложенное OP решение пропускать пробелы перед чтением:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
void readbools( istreamamp; is )
{
bool b;
while( is >> ws amp;amp; !is.eof() amp;amp; is >> b ) // <- Proposed scheme.
{
cout << (b ? "T" : "F") << endl;
}
if( is.fail() )
{
cerr << "!readbools: failed to extract 'bool' value." << endl;
}
cout << "- " << is.good() << is.eof() << is.fail() << is.bad() << "n";
}
void testread( string constamp; s )
{
istringstream is( s );
is >> boolalpha;
readbools( is );
}
int main()
{
cout << string( 60, '-' ) << endl;
testread( "true false" );
cout << string( 60, '-' ) << endl;
testread( "true false tr" );
cout << string( 60, '-' ) << endl;
testread( "true false truex" );
}
Пример результата:
------------------------------------------------------------ T F - 0100 ------------------------------------------------------------ T F !readbools: не удалось извлечь значение 'bool'. - 0110 ------------------------------------------------------------ T F T !readbools: не удалось извлечь значение 'bool'. - 0010
Основное отличие заключается в том, что этот подход выдает 3 успешно прочитанных значения в третьем случае, даже если третье значение указано неправильно (как "truex"
).
Т.е. ему не удается распознать неправильную спецификацию как таковую.
Конечно, моя способность писать код, который не работает ™, не является доказательством того, что он не может работать. Но я довольно хорош в кодировании вещей, и я не мог видеть никакого способа определить "truex"
как неправильный при таком подходе (в то время как это было легко сделать с подходом, основанным на исключениях чтения слов). Итак, по крайней мере, для меня подход, основанный на исключении прочитанных слов, проще в том смысле, что легко заставить его вести себя правильно.
Комментарии:
1. Этого недостаточно, потому что EOF может быть сбит при разборе ошибочной записи. Это именно то, что демонстрирует мой пример кода: как для правильного, так и для неправильного ввода одновременно установлены eof и fail, потому что ошибка была в конце файла.
2. @AlfPSteinbach: Посмотрите еще раз на выходные данные моего примера программы: как для правильной, так и для ошибочной строки конечным битовым шаблоном было 0110, что означает, что оба
eof()
иfail()
вернули значение true. Это потому, что всеeof()
говорит вам о том, что был достигнут конец файла, а не о том, было ли успешным последнее чтение, которое достигло конца файла.3. @celtschk: этого достаточно. я добавил некоторый код, который может вам помочь.
4. @celtshk: просто проверяем eof(), в приведенном выше коде это находится в
boolFrom
функции. вам лучше задать новый вопрос о новых проблемах, которые вы поднимаете. например, вы поднимаете вопрос о том, функция синтаксического анализа или поток диктует формат ввода. в вашем вопросе эта проблема нигде не затрагивалась. если вам интересно, задайте новый вопрос SO. или, если у вас есть идеи по улучшению iostreams, например, вам не совсем понравился приведенный здесь ответ, тогда вы можете опубликовать на [comp.std.c ].5. @ApfPSteinbach: Итак, теперь, с этим дополнением, ваш ответ в основном изменился на: моя версия не верна, и ваше решение легче получить правильно (в отличие от предыдущего: ваше решение проще). Так что этот ответ я могу принять. Спасибо.