Почему извлечения потока из потока задает бит eof?
Скажем, у нас есть поток, содержащий просто:
hello
Обратите внимание, что в конце нет лишнего \n
, как это часто бывает в текстовом файле. Теперь следующий простой код показывает, что бит eof
устанавливается в потоке после извлечения одного std::string
.
int main(int argc, const char* argv[])
{
std::stringstream ss("hello");
std::string result;
ss >> result;
std::cout << ss.eof() << std::endl; // Outputs 1
return 0;
}
Однако я не понимаю, почему это произойдет в соответствии со стандартом (я читаю С++ 11 - ISO/IEC 14882: 2011 (E)). operator>>(basic_stream<...>&, basic_string<...>&)
определяется как поведение как форматированная входная функция. Это означает, что он создает объект sentry
, который переходит к пропуску пробельных символов. В этом примере их нет, поэтому конструкция sentry
завершается без проблем. При преобразовании в bool
объект sentry
дает true
, поэтому экстрактор продолжает работать с фактическим извлечением строки.
Затем извлечение определяется как:
Символы извлекаются и добавляются до тех пор, пока не произойдет следующее:
-
n
сохраняются символы - конец файла происходит во входной последовательности;
-
isspace(c,is.getloc())
верен для следующего доступного входного символа c.
После того, как последний символ (если он есть) извлечен, вызывается is.width(0), и сторожевой объект k уничтожается. Если функция не извлекает никаких символов, она вызывает is.setstate(ios::failbit)
, которая может бросать ios_base::failure
(27.5.5.4).
Ничто здесь не приводит к установке бит eof
. Да, извлечение останавливается, если оно попадает в конец файла, но оно не устанавливает бит. Фактически бит eof
должен быть установлен только в том случае, если мы делаем еще один ss >> result;
, потому что, когда sentry
пытается сохнуть пробелы, произойдет следующая ситуация:
Если is.rdbuf()->sbumpc()
или is.rdbuf()->sgetc()
возвращает traits::eof()
, функция вызывает setstate(failbit | eofbit)
Однако это пока еще не происходит, поскольку failbit
не устанавливается.
Следствие установленного бита eof
заключается в том, что единственная причина, по которой evil-idiom while (!stream.eof())
не работает при чтении файлов из-за дополнительного \n
в конце, а не потому, что eof
бит еще не установлен. Мой компилятор с радостью устанавливает бит eof
, когда извлечения останавливается в конце файла.
Так должно ли это происходить? Или стандартный означает сказать, что setstate(eofbit)
должен произойти?
Чтобы сделать это проще, соответствующие разделы стандарта:
- 21.4.8.9 Вставки и экстракторы [string.io]
- 27.7.2.2 Форматированные входные функции [istream.formatted]
- 27.7.2.1.3 Класс
basic_istream::sentry
[istream:: sentry]
Ответы
Ответ 1
std::stringstream
является basic_istream
, а operator>>
из std::string
"извлекает" из него символы (как вы узнали).
27.7.2.1 Шаблон класса basic_istream
2 Если rdbuf() → sbumpc() или rdbuf() → sgetc() возвращает черты:: eof(), то входная функция, кроме как явно указано иначе, завершает свои действия и устанавливает setstate (eofbit), которые могут вызывать ios_- base:: failure (27.5.5.4), перед возвратом.
Кроме того, "извлечение" означает вызов этих двух функций.
3 Две группы сигнатур функций-членов имеют общие свойства: форматированные входные функции (или экстракторы) и неформатированные входные функции. Обе группы входных функций описываются так, как если бы они получить (или извлечь) входные символы, вызвав rdbuf() → sbumpc() или rdbuf() → sgetc(). Они могут использовать другие общественные члены istream.
Поэтому необходимо установить eof.
Ответ 2
Интуитивно говоря, бит EOF устанавливается, потому что во время операции чтения для извлечения строки поток действительно попал в конец файла. В частности, он непрерывно считывает символы из входного потока, останавливаясь, потому что он попадает в конец потока, прежде чем сталкивается с символом пробела. Соответственно, поток установил бит EOF, чтобы отметить, что достигнут конец потока. Обратите внимание, что это не то же самое, что сообщение об ошибке - операция была успешно завершена, но точка бит EOF не сообщает о сбое. Это означало, что был встречен конец потока.
У меня нет определенной части спецификации, чтобы поддержать это, хотя я попытаюсь найти его, когда я получу шанс.
Надеюсь, это поможет!