C пока не конец файла while fin eof
Первое assert терпит неудачу, т.е. fin.fail () истинно, когда nb_try == n, что происходит, когда он пытается прочитать первое число, которое не существует. Но почему после прочтения последнего числа fin.eof () не соответствует действительности? Означает ли это, что это становится правдой только при чтении первого несуществующего числа? Верно ли, что fin.fail () и fin.eof () становятся истинными одновременно?
Спасибо и привет!
EOF — это не символ
Почему кто-то говорит или думает, что EOF — это символ? Полагаю, это может быть так из-за того, что в некоторых программах, написанных на C, можно найти код, в котором используется явная проверка на EOF с использованием функций getchar() и getc() .
Это может выглядеть так:
Если заглянуть в справку по getchar() или getc() , можно узнать, что обе функции считывают следующий символ из потока ввода. Вероятно — именно это является причиной возникновения заблуждения о природе EOF . Но это — лишь мои предположения. Вернёмся к мысли о том, что EOF — это не символ.
А что такое, вообще, символ? Символ — это самый маленький компонент текста. «A», «a», «B», «b» — всё это — разные символы. У символа есть числовой код, который в стандарте Unicode называют кодовой точкой. Например — латинская буква «A» имеет, в десятичном представлении, код 65. Это можно быстро проверить, воспользовавшись командной строкой интерпретатора Python:
Или можно взглянуть на таблицу ASCII в Unix/Linux:
Выясним, какой код соответствует EOF , написав небольшую программу на C. В ANSI C константа EOF определена в stdio.h , она является частью стандартной библиотеки. Обычно в эту константу записано -1 . Можете сохранить следующий код в файле printeof.c , скомпилировать его и запустить:
Скомпилируем и запустим программу:
У меня эта программа, проверенная на Mac OS и на Ubuntu, сообщает о том, что EOF равняется -1 . Есть ли какой-нибудь символ с таким кодом? Тут, опять же, можно проверить коды символов в таблице ASCII, можно взглянуть на таблицу Unicode и узнать о том, в каком диапазоне могут находиться коды символов. Мы же поступим иначе: запустим интерпретатор Python и воспользуемся стандартной функцией chr() для того, чтобы она дала бы нам символ, соответствующий коду -1 :
Как и ожидалось, символа с кодом -1 не существует. Значит, в итоге, EOF , и правда, символом не является. Переходим теперь ко второму рассматриваемому утверждению.
В конце файлов нет некоего особого символа
Может, EOF — это особенный символ, который можно обнаружить в конце файла? Полагаю, сейчас вы уже знаете ответ. Но давайте тщательно проверим наше предположение.
Возьмём простой текстовый файл, helloworld.txt, и выведем его содержимое в шестнадцатеричном представлении. Для этого можно воспользоваться командой xxd :
Как видите, последний символ файла имеет код 0a . Из таблицы ASCII можно узнать о том, что этот код соответствует символу nl , то есть — символу новой строки. Это можно выяснить и воспользовавшись Python:
Так. EOF — это не символ, а в конце файлов нет некоего особого символа. Что же такое EOF ?
Что такое EOF?
EOF (end-of-file) — это состояние, которое может быть обнаружено приложением в ситуации, когда операция чтения файла доходит до его конца.
Взглянем на то, как можно обнаруживать состояние EOF в разных языках программирования при чтении текстового файла с использованием высокоуровневых средств ввода-вывода, предоставляемых этими языками. Для этого напишем очень простую версию cat , которая будет называться mcat . Она побайтно (посимвольно) читает ASCII-текст и в явном виде выполняет проверку на EOF . Программу напишем на следующих языках:
- ANSI C
- Python 3
- Go
- JavaScript (Node.js)
3 ответа
Это неправильный способ чтения файла:
Итак, быстро взглянув на ваш код, я думаю, что было бы проще написать его как:
Проблема в том, что EOF верен только ПОСЛЕ , когда вы пытаетесь прочитать его. Отсутствие в файле символа для чтения - это не то же самое, что истинность EOF. Итак, вы читаете последнюю строку и получаете значения, и читать нечего, но EOF по-прежнему ложно, поэтому код повторно входит в цикл. Когда он пытается прочитать c_tmp, срабатывает EOF, и ваши утверждения становятся грушевидными.
Решение состоит в том, чтобы поместить чтение как условие while. Результатом чтения является поток. Но когда поток используется в логическом контексте (таком как условие while), он преобразуется в тип, который можно использовать как bool (технически это void *, но это не важно).
Спасибо! когда именно fin.eof () становится истинным? После прочтения последнего числа в файле или после попытки прочитать первое несуществующее число? Тот же вопрос для fin.fail ()?
EOF является истинным, когда вы пытаетесь прочитать после EOF. Чтение до конца файла не меняет состояния. Таким образом, EOF становится истинным, когда вы читаете первое значение, которое не существует после конца файла.
Бит ошибки устанавливается, если последняя операция не удалась. В этом случае fail и eof станут истинными одновременно. Но это не означает, что fail и eof - одно и то же. Обратите внимание, что ошибка будет установлена, если вы попытаетесь прочитать нечисловую строку в переменной типа int с помощью оператора >> и т.д.
IIRC, eofbit не устанавливается, пока вы действительно не попытаетесь прочитать конец файла. То есть, когда вы дойдете до конца файла, вам нужно будет прочитать еще раз, прежде чем этот флаг будет установлен.
Если текстовый файл содержит эту последовательность без кавычек, «12345 67890», то №3 вернет ложь, а №4 вернет истину, потому что после последнего числа нет пробелов:
Вы должны проверить оба, eof () и fail (), и если оба верны, у вас больше нет данных. Если fail () истинно, а eof () ложно, проблема с файлом.
That's my code, basically it does exactly what I want it to but it keeps reading when the file is not good anymore. It'll input and output all the things I'm looking for, and then when the file is at an end, fin.good() apparently isn't returning false. Here is the text file.
and here is the output
Here's is Gtable's type.
I'm expecting it to stop after outputting 'E' which is the last char in the file. The program never gets into the for loop again after reading the last char. I can't figure out why the while isn't breaking.
why do you think it segfaults when reading? what is Gtable? isnt it more likely that you access a nonexisting index? what did the debugger tell you about the exact place and instruction leading to the segfault?
It may segfault from something else, but I don't want to do any operations once the file is finished regardless. I'm pretty sure it segfaults while trying to read an empty part of the file. Gtable is an array of objects. This array is initialized to 100 slots for no reason other than to make sure that I'm not going out of index!
@TylerPaff: well, did you know that any kind of eof() or good() only gets set after a read failed, and that you usually should check whether a read has succeeded, and after that ask for the reason via eof() ?
You do know the standard C++ library provides automatically resizing containers, yeah? Why are you mucking around with clumsy and error-prone arrays? Also, could we see the definition of whatever it is that Gtable is an array of?
Python 3
В Python нет механизма явной проверки на EOF , похожего на тот, который имеется в ANSI C. Но если посимвольно читать файл, то можно выявить состояние EOF в том случае, если в переменной, хранящей очередной прочитанный символ, будет пусто:
Запустим программу и взглянём на возвращаемые ей результаты:
Вот более короткая версия этого же примера, написанная на Python 3.8+. Здесь используется оператор := (его называют «оператор walrus» или «моржовый оператор»):
Запустим этот код:
В Go можно явным образом проверить ошибку, возвращённую Read(), на предмет того, не указывает ли она на то, что мы добрались до конца файла:
3 ответа
Прежде всего, я предполагаю, что fin является вашим fstream объектом. В этом случае ваш учитель не сказал бы вам использовать while(fin.eof()) для чтения из файла. Она бы сказала использовать while(!fin.eof()) .
Позволь мне объяснить. eof() является членом класса fstream , который возвращает значение true или false в зависимости от того, был ли достигнут конец файла (eof) файла, который вы читаете , Таким образом, хотя функция eof() возвращает 0 , это означает, что конец файла не достигнут и цикл продолжает выполняться, но когда eof() возвращает 1 , конец файла был Достигнута и петля выходит.
Цикл while(fin) введен, потому что fin фактически возвращает значение переменной флага ошибки внутри объекта класса fin , значение которого равно 0, когда любая функция, такая как чтение, запись или открытие, завершается неудачей. Таким образом, цикл работает до тех пор, пока работает функция чтения внутри цикла.
Лично я бы не предложил ни одного из них. Я бы предложил
// предположим, что класс abc.
Этот цикл читает запись файла внутри условия цикла, и если ничего не было прочитано из-за достижения конца файла, цикл завершается.
Проблема с while(!fin.eof()) состоит в том, что он возвращает 1 , если достигнут конец файла. Конец файла - это символ, который помещается в конец файла. Поэтому, когда функция чтения внутри цикла читает этот символ и устанавливает переменную eof в 1. Все, что фактически делает функция - это возвращает это значение.
Это прекрасно работает, когда вы читаете строки в словах, но когда вы читаете последовательные записи класса из файла, этот метод завершится ошибкой. Рассматривать
Таким образом отображается последняя запись дважды. Это связано с тем, что символ конца файла является символом после последнего байта последней записи. Когда последний прочитан, указатель файла находится в байте eof, но еще не прочитал его. Таким образом, он снова войдет в цикл, но на этот раз eof char будет прочитан, но функция read не будет выполнена. Значения уже в переменной a , то есть предыдущие записи, будут отображены снова.
Один хороший метод - сделать что-то вроде этого:
Или в случае текстового файла:
Здесь объект читается в условии условия цикла while, а затем проверяется значение флага eof . Это не приведет к нежелательным результатам.
Здесь мы проверяем состояние фактической операции ввода-вывода и eof вместе. Вы также можете проверить флаг сбоя.
Я хотел бы отметить, что согласно @RetiredNinja, мы можем проверять только операцию ввода-вывода. Это:
Быстрый и простой обходной путь, который помог мне избежать проблем при использовании eof, - это проверить его после первого чтения, а не как условие самого цикла while. Что-то вроде этого:
JavaScript (Node.js)
В среде Node.js нет механизма для явной проверки на EOF . Но, когда при достижении конца файла делается попытка ещё что-то прочитать, вызывается событие потока end.
Низкоуровневые системные механизмы
Как высокоуровневые механизмы ввода-вывода, использованные в вышеприведённых примерах, определяют достижение конца файла? В Linux эти механизмы прямо или косвенно используют системный вызов read(), предоставляемый ядром. Функция (или макрос) getc() из C, например, использует системный вызов read() и возвращает EOF в том случае, если read() указывает на возникновение состояния достижения конца файла. В этом случае read() возвращает 0 . Если изобразить всё это в виде схемы, то получится следующее:
Получается, что функция getc() основана на read() .
Напишем версию cat , названную syscat , используя только системные вызовы Unix. Сделаем мы это не только из интереса, но и из-за того, что это вполне может принести нам какую-то пользу.
Вот эта программа, написанная на C:
В этом коде используется тот факт, что функция read() , указывая на достижение конца файла, возвращает 0 .
Мне сказали, что я должен использовать while(fin) вместо while(!fin.eof()) при чтении файла.
В чем именно разница?
Правка . Я знаю, что while(fin) на самом деле проверяет объект потока и что, когда он становится NULL, цикл прерывается и закрывает флаги eof и fail. Но мой преподаватель курса говорит, что fin.eof() лучше, поэтому мне нужно понять основную операцию, которая здесь происходит.
Какой из них является правильной практикой?
Примечание. Это не дубликат, мне нужна помощь в Turbo C ++ и в двоичных файлах. Я в основном пытаюсь прочитать файл, используя объект класса.
3 Answers 3
First of all I am assuming fin is your fstream object. In which case your teacher would not have told you to use while(fin.eof()) for reading from file. She would have told to use while(!fin.eof()) .
Let me explain. eof() is a member of the fstream class which returns a true or false value depending on whether the End Of File (eof) of the file you are reading has been reached. Thus while eof() function returns 0 it means the end of file has not been reached and loop continues to execute, but when eof() returns 1 the end of the file has been reached and the loop exits.
while(fin) loop is entered because fin actually returns the value of an error flag variable inside the class object fin whose value is set to 0 when any function like read or write or open fails. Thus the loop works as long as the read function inside the loop works.
Personally I would not suggest either of them. I would suggest
//assume a class abc.
This loop reads the file record inside the loop condition and if nothing was read due to the end of file being reached, the loop is exited.
The problem with while(!fin.eof()) is that it returns 1 if the end of file has been reached. End of file is actually a character that is put at the end of the file. So when the read function inside the loop reads this character and sets a variable eof to 1. All the function actually does is return this value.
Thus works fine when you are reading lines in words but when you are reading successive records of a class from a file, this method will fail. Consider
Thus displays the last record twice. This is because the end of file character is the character after the last byte of the last record. When the last is read the file pointer is at the eof byte but hasn't read it yet. So it will enter the loop again but this time the eof char is read but the read function fails. The values already in the variable a , that is the previous records will be displayed again.
Недавно я читал книгу «Компьютерные системы: архитектура и программирование. Взгляд программиста». Там, в главе про систему ввода-вывода Unix, авторы упомянули о том, что в конце файла нет особого символа EOF .
Если вы читали о системе ввода-вывода Unix/Linux, или экспериментировали с ней, если писали программы на C, которые читают данные из файлов, то это заявление вам, вероятно, покажется совершенно очевидным. Но давайте поближе присмотримся к следующим двум утверждениям, относящимся к тому, что я нашёл в книге:
- EOF — это не символ.
- В конце файлов нет некоего особого символа.
5 Answers 5
Your condition in the while loop is wrong. ios::eof() isn't predictive; it will only be set once the stream has attempted (internally) to read beyond end of file. You have to check after each
input.
The classical way of handling your case would be to define a >> function for GTable , along the lines of:
(This was written rather hastily. In real code, I'd almost certainly factor the inner loop out into a separate function.)
We read line by line, in order to verify the format (and to allow resynchronization in case of error).
We set failbit in the source stream in case of an input error.
We skip empty lines (since your input apparently contains them).
We do not modify the target element until we are sure that the input is correct.
One we have this, it is easy to loop over all of the elements:
I'll point this out explicitly, because the other people responding seem to have missed it: it is absolutely imperative to ensure that you have the place to read into before attempting the read.
Given the number of wrong answers this question is receiving, I would like to stress:
Any use of fin.eof() before the input is known to fail is wrong.
Any use of fin.good() , period, is wrong.
Any use of one of the values read before having tested that the input has succeeded is wrong. (This doesn't prevent things like fin >> a >> b , as long as neither a or b are used before the success is tested.)
Any attempt to read into Gtable[slot] without ensuring that slot is in bounds is wrong.
With regards to eof() and good() :
The base class of istream and ostream defines three “error” bits: failbit , badbit and eofbit . It's important to understand when these are set: badbit is set in case of a non-recoverable hardward error (practically never, in fact, since most implementations can't or don't detect such errors); and failbit is set in any other case the input fails—either no data available (end of file), or a format error ( "abc" when inputting an int, etc.). eofbit is set anytime the streambuf returns EOF , whether this causes the input to fail or not! Thus, if you read an int , and the stream contains "123" , without trailing white space or newline, eofbit will be set (since the stream must read ahead to know where the int ends); if the stream contains "123\n" , eofbit will not be set. In both cases, however, the input succeeds, and failbit will not be set.
To read these bits, there are the following functions (as code, since I don't know how to get a table otherwise):
Given this: the first check must always be fail() or one of the operator (which are based on fail ). Once fail() returns true, we can use the other functions to determine why:
Practically speaking, any other use is an error (and any use of good() is an error—don't ask me why the function is there).
I was told that I should be using while(fin) instead of while(!fin.eof()) when reading a file.
What exactly is the difference?
Edit: I do know that while(fin) actually checks the stream object and that when it becomes NULL, the loop breaks and it covers eof and fail flags. But my course teacher says that fin.eof() is better so I need to understand the fundamental operation that's going on here.
Which one is the right practice?
Note: This is not a duplicate, I need assistance in Turbo C++ and with binary files. I'm basically trying to read a file using a class object.
@CodesInChaos I'm looking for some theoretical explanation. I can of course check fin and fin.eof(). And it turns out that fin also checks for the fail flag along with the eof flag but I don't understand how it works.
@AlphaMineron In your comment you already describe what the difference is, so what are you asking for? It directly follows that while(fin.eof()) will enter an endless loop if an error is encountered, while while(fin) exits the loop.
The stream object doesn't actually become null. It enters an invalid state, and after that its operator void*() function returns null because it's in an invalid state. (In modern C++, it's explicit operator bool() instead, but Turbo C++ is definitely not modern.)
ANSI C
Начнём с почтенного C. Представленная здесь программа является модифицированной версией cat из книги «Язык программирования C».
Вот некоторые пояснения, касающиеся вышеприведённого кода:
- Программа открывает файл, переданный ей в виде аргумента командной строки.
- В цикле while осуществляется копирование данных из файла в стандартный поток вывода. Данные копируются побайтово, происходит это до тех пор, пока не будет достигнут конец файла.
- Когда программа доходит до EOF , она закрывает файл и завершает работу.
Читайте также: