Контроль ошибок открытия файлов функции good eof
БлогNot. Лекции по C/C++: работа с файлами (fstream)
Лекции по C/C++: работа с файлами (fstream)
Механизм ввода-вывода, разработанный для обычного языка С, не соответствует общепринятому сегодня стилю объектно-ориентированного программирования, кроме того, он активно использует операции с указателями, считающиеся потенциально небезопасными в современных защищённых средах выполнения кода. Альтернативой при разработке прикладных приложений является механизм стандартных классов ввода-вывода, предоставляемый стандартом языка C++.
Открытие файлов
Наиболее часто применяются классы ifstream для чтения, ofstream для записи и fstream для модификации файлов.
Все поточные классы ввода-вывода являются косвенными производными от общего предка ios , полностью наследуя его функциональность. Так, режим открытия файлов задает член данных перечисляемого типа open_mode, который определяется следующим образом:
Ниже приведены возможные значения флагов и их назначение.
Режим | Назначение |
in | Открыть для ввода (выбирается по умолчанию для ifstream) |
out | Открыть для вывода (выбирается по умолчанию для ofstream) |
binary | Открыть файл в бинарном виде |
aрр | Присоединять данные; запись в конец файла |
ate | Установить файловый указатель на конец файла |
trunc | Уничтожить содержимое, если файл существует (выбирается по умолчанию, если флаг out указан, а флаги ate и арр — нет) |
Например, чтобы открыть файл с именем test.txt для чтения данных в бинарном виде, следует написать:
Оператор логического ИЛИ ( | ) позволяет составить режим с любым сочетанием флагов. Так, чтобы, открывая файл по записи, случайно не затереть существующий файл с тем же именем, надо использовать следующую форму:
Предполагается, что к проекту подключён соответствующий заголовочный файл:
Для проверки того удалось ли открыть файл, можно применять конструкцию
Операторы включения и извлечения
Переопределённый в классах работы с файлами оператор включения ( << ) записывает данные в файловый поток. Как только вы открыли файл для записи, можно записывать в него текстовую строку целиком:
Можно также записывать текстовую строку по частям:
Оператор endl завершает ввод строки символом "возврат каретки":
С помощью оператора включения несложно записывать в файл значения переменных или элементов массива:
В результате выполнения кода образуется три строки текстового файла Temp.txt :
Обратите внимание, что числовые значения записываются в файл в виде текстовых строк, а не двоичных значений.
Оператор извлечения ( >> )производит обратные действия. Казалось бы, чтобы извлечь символы из файла Temp.txt , записанного ранее, нужно написать код наподобие следующего:
Однако оператор извлечения остановится на первом попавшемся разделителе (символе пробела, табуляции или новой строки). Таким образом, при разборе предложения "Текстовый массив содержит переменные" только слово "Текстовый" запишется в массив buff , пробел игнорируется, а слово "массив" станет значением целой переменной vx и исполнение кода "пойдет вразнос" с неминуемым нарушением структуры данных. Далее, при обсуждении класса ifstream , будет показано, как правильно организовать чтение файла из предыдущего примера.
Класс ifstream: чтение файлов
Как следует из расшифровки названия, класс ifstream предназначен для ввода файлового потока. Далее перечислены основные методы класса. Большая часть из них унаследована от класса istream и перегружена с расширением родительской функциональности. К примеру, функция get , в зависимости от параметра вызова, способна считывать не только одиночный символ, но и символьный блок.
Метод | Описание |
open | Открывает файл для чтения |
get | Читает один или более символов из файла |
getline | Читает символьную строку из текстового файла или данные из бинарного файла до определенного ограничителя |
read | Считывает заданное число байт из файла в память |
eof | Возвращает ненулевое значение (true), когда указатель потока достигает конца файла |
peek | Выдает очередной символ потока, но не выбирает его |
seekg | Перемещает указатель позиционирования файла в заданное положение |
tellg | Возвращает текущее значение указателя позиционирования файла |
close | Закрывает файл |
Теперь понятно, как нужно модифицировать предыдущий пример, чтобы использование оператора извлечения данных давало ожидаемый результат:
Метод getline прочитает первую строку файла до конца, а оператор >> присвоит значения переменным.
Следующий пример показывает добавление данных в текстовый файл с последующим чтением всего файла. Цикл while (1) используется вместо while(!file2.eof()) по причинам, которые обсуждались в предыдущей лекции.
Этот код под ОС Windows также зависит от наличия в последней строке файла символа перевода строки, надежнее было бы сделать так:
Явные вызовы методов open и close не обязательны. Действительно, вызов конструктора с аргументом позволяет сразу же, в момент создания поточного объекта file , открыть файл:
Вместо метода close можно использовать оператор delete , который автоматически вызовет деструктор объекта file и закроет файл. Код цикла while обеспечивает надлежащую проверку признака конца файла.
Класс ofstream: запись файлов
Класс ofstream предназначен для вывода данных из файлового потока. Далее перечислены основные методы данного класса.
Метод | Описание |
open | Открывает файл для записи |
put | Записывает одиночный символ в файл |
write | Записывает заданное число байт из памяти в файл |
seekp | Перемещает указатель позиционирования в указанное положение |
tellp | Возвращает текущее значение указателя позиционирования файла |
close | Закрывает файл |
Описанный ранее оператор включения удобен для организации записи в текстовый файл:
Бинарные файлы
В принципе, бинарные данные обслуживаются наподобие текстовых. Отличие состоит в том, что если бинарные данные записываются в определенной логической структуре, то они должны считываться из файла в переменную того же структурного типа.
Первый параметр методов write и read (адрес блока записи/чтения) должен иметь тип символьного указателя char * , поэтому необходимо произвести явное преобразование типа адреса структуры void * . Второй параметр указывает, что бинарные блоки файла имеют постоянный размер байтов независимо от фактической длины записи. Следующее приложение дает пример создания и отображения данных простейшей записной книжки. Затем записи файла последовательно считываются и отображаются на консоли.
P.S. При выполнении этого и других листингов в Visual Studio последних версий может дополнительно понадобиться подключение директивы _CRT_SECURE_NO_WARNINGS.
В результате выполнения этого кода образуется бинарный файл Notebook.dat из трех блоков размером по 80 байт каждый (при условии, что символы - однобайтовые). Естественно, вы можете использовать другие поточные методы и проделывать любые операции над полями определенной структуры данных.
Класс fstream: произвольный доступ к файлу
Предположим что в нашей записной книжке накопилось 100 записей, а мы хотим считать 50-ю. Конечно, можно организовать цикл и прочитать все записи с первой по заданную. Очевидно, что более целенаправленное решение - установить указатель позиционирования файла pos прямо на запись 50 и считать ее:
Подобные операции поиска эффективны, если файл состоит из записей известного и постоянного размера. Чтобы заменить содержимое произвольной записи, надо открыть поток вывода в режиме модификации:
Если не указать флаг ios::ate (или ios::app ), то при открытии бинарного файла Notebook.dat его предыдущее содержимое будет стерто!
Дополнительно может понадобиться указать, откуда отсчитывается смещение.
Наконец, можно открыть файл одновременно для чтения/записи, используя методы, унаследованные поточным классом fstream от своих предшественников. Поскольку класс fstream произведен от istream и ostream (родителей ifstream и ofstream соответственно), все упомянутые ранее методы становятся доступными в приложении.
Недавно я читал книгу «Компьютерные системы: архитектура и программирование. Взгляд программиста». Там, в главе про систему ввода-вывода Unix, авторы упомянули о том, что в конце файла нет особого символа EOF .
Если вы читали о системе ввода-вывода Unix/Linux, или экспериментировали с ней, если писали программы на C, которые читают данные из файлов, то это заявление вам, вероятно, покажется совершенно очевидным. Но давайте поближе присмотримся к следующим двум утверждениям, относящимся к тому, что я нашёл в книге:
- 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)
ANSI C
Начнём с почтенного C. Представленная здесь программа является модифицированной версией cat из книги «Язык программирования C».
Вот некоторые пояснения, касающиеся вышеприведённого кода:
- Программа открывает файл, переданный ей в виде аргумента командной строки.
- В цикле while осуществляется копирование данных из файла в стандартный поток вывода. Данные копируются побайтово, происходит это до тех пор, пока не будет достигнут конец файла.
- Когда программа доходит до EOF , она закрывает файл и завершает работу.
Python 3
В Python нет механизма явной проверки на EOF , похожего на тот, который имеется в ANSI C. Но если посимвольно читать файл, то можно выявить состояние EOF в том случае, если в переменной, хранящей очередной прочитанный символ, будет пусто:
Запустим программу и взглянём на возвращаемые ей результаты:
Вот более короткая версия этого же примера, написанная на Python 3.8+. Здесь используется оператор := (его называют «оператор walrus» или «моржовый оператор»):
Запустим этот код:
В Go можно явным образом проверить ошибку, возвращённую Read(), на предмет того, не указывает ли она на то, что мы добрались до конца файла:
JavaScript (Node.js)
В среде Node.js нет механизма для явной проверки на EOF . Но, когда при достижении конца файла делается попытка ещё что-то прочитать, вызывается событие потока end.
Низкоуровневые системные механизмы
Как высокоуровневые механизмы ввода-вывода, использованные в вышеприведённых примерах, определяют достижение конца файла? В Linux эти механизмы прямо или косвенно используют системный вызов read(), предоставляемый ядром. Функция (или макрос) getc() из C, например, использует системный вызов read() и возвращает EOF в том случае, если read() указывает на возникновение состояния достижения конца файла. В этом случае read() возвращает 0 . Если изобразить всё это в виде схемы, то получится следующее:
Получается, что функция getc() основана на read() .
Напишем версию cat , названную syscat , используя только системные вызовы Unix. Сделаем мы это не только из интереса, но и из-за того, что это вполне может принести нам какую-то пользу.
Вот эта программа, написанная на C:
В этом коде используется тот факт, что функция read() , указывая на достижение конца файла, возвращает 0 .
На этом уроке мы рассмотрим состояния потока, валидацию пользовательского ввода и какой она бывает, а также нюансы, связанные с этой темой в языке С++.
Состояния потока
Класс ios_base содержит следующие флаги для обозначения состояния потоков:
goodbit — всё хорошо;
badbit — произошла какая-то фатальная ошибка (например, программа попыталась прочитать данные после конца файла);
eofbit — поток достиг конца файла;
failbit — произошла какая-то НЕ фатальная ошибка (например, пользователь ввел буквы, когда программа ожидала числа).
Хотя эти флаги находятся в ios_base , но поскольку ios является дочерним классом для ios_base , доступ к этим флагам также возможен и через ios (например, как std::ios::failbit ).
ios также предоставляет ряд методов для доступа к вышеперечисленным состояниям потока:
good() — возвращает true , если установлен goodbit (значит, что с потоком всё ок);
bad() — возвращает true , если установлен badbit (значит, что произошла какая-то фатальная ошибка);
eof() — возвращает true , если установлен eofbit (значит, что поток находится в конце файла);
fail() — возвращает true , если установлен failbit (значит, что произошла какая-то НЕ фатальная ошибка);
clear() — сбрасывает все текущие флаги состояния потока и задает ему goodbit;
clear(state) — сбрасывает все текущие флаги состояния потока и устанавливает флаг, переданный в качестве параметра (state);
rdstate() — возвращает текущие установленные флаги;
setstate(state) — устанавливает флаг состояния, переданный в качестве параметра (state).
Чаще всего мы будем иметь дело с failbit, который срабатывает при некорректном пользовательском вводе. Например:
Обратите внимание, эта программа ожидает от пользователя ввод целого числа. Однако, если пользователь введет что-либо другое (например, Tom ), то cin не сможет извлечь что-либо в nAge , и для потока будет установлен флаг failbit.
Валидация пользовательского ввода
Валидация пользовательского ввода — это процесс проверки того, соответствует ли пользовательский ввод заданным критериям. Обычно, валидация ввода бывает числовой и строковой.
Со строковой валидацией мы принимаем весь пользовательский ввод в качестве строки, а затем либо принимаем эту строку, либо отклоняем её (в зависимости от критериев проверки). Например, если мы просим пользователя ввести номер телефона, то мы должны убедиться, что этот номер состоит из 10 цифр. В большинстве языков (особенно в скриптовых, таких как Perl и PHP) это можно сделать с помощью регулярных выражений. В языке C++ нет встроенной поддержки регулярных выражений (возможно, это добавят в следующих версиях языка C++), поэтому обычно это делается путем проверки каждого символа строки на соответствие заданным критериям.
С числовой валидацией мы обычно заботимся о том, чтобы число, которое ввел пользователь, находилось в определенном диапазоне (например, от 0 до 20). Однако, в отличие от строковой валидации, пользователь может ввести данные, которые вообще не являются числами, а нам нужно будет обрабатывать и такие случаи.
Для решения этой проблемы C++ предоставляет ряд полезных функций, которые мы можем использовать для определения того, являются ли конкретные символы цифрами или буквами. Следующие функции находятся в заголовочном файле cctype:
функция isalnum(int) — возвращает ненулевое значение, если параметром является буква или цифра;
функция isalpha(int) — возвращает ненулевое значение, если параметром является буква;
функция iscntrl(int) — возвращает ненулевое значение, если параметром является управляющий символ;
функция isdigit(int) — возвращает ненулевое значение, если параметром является цифра;
функция isgraph(int) — возвращает ненулевое значение, если параметром является выводимый символ (но не пробел);
функция isprint(int) — возвращает ненулевое значение, если параметром является выводимый символ, включая пробел;
функция ispunct(int) — возвращает ненулевое значение, если параметром не являются ни буква, ни цифра, ни пробел;
функция isspace(int) — возвращает ненулевое значение, если параметром является пробел;
функция isxdigit(int) — возвращает ненулевое значение, если параметром является шестнадцатеричная цифра ( 0-9 , a-f , A-F ).
Строковая валидация
Давайте выполним простую строковую валидацию, попросив пользователя ввести свое имя. Наши ограничения: имя может содержать только буквы и пробелы. Если пользователь введет что-либо лишнее, то ввод отклоняется. Перебирать и проверять мы будем каждый символ пользовательского ввода:
Для чего нужен с++?
Я школьник, мне 16 лет, учусь в 10 классе, планирую поступить на факультет защиты информационных.
Для чего нужен конструктор?
если несложно, то объясните для чего нужен конструктор? я прочитал, что он требуется для.
Для чего нужен break?
для чего нужен break?
В компьютерной терминологии end-of-file (конец файла) или просто EOF, является индикатором операционной системы означающим, что данные в источнике закончились. Источниками данных, обычно, являются файлы и потоки. может кто нибудь обьяснить для чего здесь нужен EOF (end of file),
Не нужен он тут особо , возьми и организуй цикл по другому
fasked , получаутся что-то вроде бесконечного цикла.
!cin.eof() нужно чтобы прекратить цикл при прекращение ввода если бы в условии было ch = cin.get() то цикл бы зациклился ведь false это 0 а eof на большинстве систем -1. таким образом !cin.eof() возвращает 0 только при достижении конца потока/файла.
Добавлено через 12 минут
ещё можно так написать
Я не это имел ввиду просто cin.eof свидетельствует о прекращение ввода и
while (ch = cin.get()) != EOF)
по сути эквивалентно
while (!cin.eof())
ch = cin.get();
Решение
ошибка чтения вроде не выставляет eof флаг, is_good может не вариант, но eof точно не вариантПоправочка, не is_good, а просто good.
ios::good - false, если любой из флагов состояний (eofbit, failbit, badbit) активен, то есть и при достижении конца потока/файла и при ошибки чтения/записи.
ios::fail - true, если любой из флагов состояний failbit или badbit активен, то есть проверка только на ошибки.
ios::bad - проверка только флага badbit.
Сами же флаги состояний описывают следующие свойства:
eofbit - конец потока/файла.
failbit - последняя операция завершилась неуспешно в связи с ошибкой в логике самой операции.
badbit - операция завершена неуспешно в связи с отказом записи (или чтения) в поток.
goodbit - нет никаких ошибок.
Поправьте, если не прав.
!cin.eof() нужно чтобы прекратить цикл при прекращение ввода Он только eofbit проверяет. А если cin - файл с другого компа или с флешки, а сеть отключилась и флешку вынули, eofbit'а не будет. Будет badbit. задание такое:написать программу, печатающую все вводимые символы в верхнем регистре как я это понял имелся ввиду именно ввод из консоли а не считывание файла или сокета.Вопрос что такое .eof() а не что использовать.
И мой пример показывает как он работает.(про eofbit никто не спрашивал).
Вывод !cin.eof() для определения конца ввода.(по ctrl d или ctrl z(или что там в windows)). В cin может и файл загоняться: myprog.exe < my.text
Ну и как выше написали сработает на Ctrl-Z или Ctrl-D в зависимости от системы, если вводить руками. Можно и так. +А почему в этом примере cin.getline() не работает:
Добавлено через 10 минут
П.С. Это не самый лучший способ для С++ потоков.
Вопрос зачем .eof()
Ответ для определения конца ввода
Поправочка, не is_good, а просто good.
ios::good - false, если любой из флагов состояний (eofbit, failbit, badbit) активен, то есть и при достижении конца потока/файла и при ошибки чтения/записи.
ios::fail - true, если любой из флагов состояний failbit или badbit активен, то есть проверка только на ошибки.
ios::bad - проверка только флага badbit.
Сами же флаги состояний описывают следующие свойства:
eofbit - конец потока/файла.
failbit - последняя операция завершилась неуспешно в связи с ошибкой в логике самой операции.
badbit - операция завершена неуспешно в связи с отказом записи (или чтения) в поток.
goodbit - нет никаких ошибок.
EOF означает End Of File . Это момент в программе, когда пользователь больше не может читать данные. Это означает, что программа читает весь файл до конца. Кроме того, при достижении EOF или конца файла в качестве вывода возвращаются пустые строки. Итак, пользователю необходимо знать, находится ли файл в его EOF.
В этом руководстве представлены различные способы узнать, находится ли файл в его EOF в Python.
Используйте file.read() , чтобы найти конец файла в Python
Метод file.read() - это встроенная функция Python, используемая для чтения содержимого данного файла. Если метод file.read() возвращает пустую строку в качестве вывода, это означает, что файл достиг своего EOF.
Обратите внимание, что когда мы вызываем функцию open() для открытия файла при запуске программы, мы используем "r" как режим только для чтения файла. Наконец, мы используем условный оператор if , чтобы проверить, что возвращаемый результат - это пустая строка.
Используйте метод readline() с циклом while для поиска конца файла в Python
Метод file.readline() - еще одна встроенная функция Python для чтения одной полной строки текстового файла.
Цикл while в Python - это цикл, который повторяет данное условие в блоке кода до тех пор, пока данное условие не станет истинным. Этот цикл используется, когда количество итераций заранее не известно.
Использование цикла while с методом readline() помогает многократно читать строки в данном текстовом файле.
Цикл while прекратит итерацию, когда в текстовом файле не останется текста для чтения методом readline() .
Использование оператора Walrus для поиска конца файла в Python
Оператор Walrus - новый оператор в Python 3.8. Обозначается := . Этот оператор по сути является оператором присваивания, который используется для присвоения значений True и их немедленной печати.
Здесь значения True - это символы, которые функция read() будет читать из текстового файла. Это означает, что оператор моржа прекратит печать после завершения файла.
Сопутствующая статья - Python File
Читайте также: