Ofstream c что это
Для работы с файлами в стандартной библиотеке определен заголовочный файл fstream , который определяет базовые типы для чтения и записи файлов. В частности, это:
ifstream : для чтения с файла
ofstream : для записи в файл
fstream : совмещает запись и чтение
Для работы с данными типа wchar_t для этих потоков определены двойники:
Открытие файла
При операциях с файлом вначале необходимо открыть файл с помощью функции open() . Данная функция имеет две версии:
Для открытия файла в функцию необходимо передать путь к файлу в виде строки. И также можно указать режим открытия. Список доступных режимов открытия файла:
ios::in : файл открывается для ввода (чтения). Может быть установлен только для объекта ifstream или fstream
ios::out : файл открывается для вывода (записи). При этом старые данные удаляются. Может быть установлен только для объекта ofstream или fstream
ios::app : файл открывается для дозаписи. Старые данные не удаляются.
ios::ate : после открытия файла перемещает указатель в конец файла
ios::trunc : файл усекается при открытии. Может быть установлен, если также установлен режим out
ios::binary : файл открывается в бинарном режиме
Если при открытии режим не указан, то по умолчанию для объектов ofstream применяется режим ios::out , а для объектов ifstream — режим ios::in . Для объектов fstream совмещаются режимы ios::out и ios::in .
Однако в принципе необязательно использовать функцию open для открытия файла. В качестве альтернативы можно также использовать конструктор объектов-потоков и передавать в них путь к файлу и режим открытия:
При вызове конструктора, в который передан путь к файлу, данный файл будет автоматически открываться:
Вообще использование конструкторов для открытия потока является более предпочтительным, так как определение переменной, представляющей файловой поток, уже преполагает, что этот поток будет открыт для чтения или записи. А использование конструктора избавит от ситуации, когда мы забудем открыть поток, но при этом начнем его использовать.
В процессе работы мы можем проверить, окрыт ли файл с помощью функции is_open() . Если файл открыт, то она возвращает true:
Закрытие файла
После завершения работы с файлом его следует закрыть с помощью функции close() . Также стоит отметить, то при выходе объекта потока из области видимости, он удаляется, и у него автоматически вызывается функция close.
Стоит отметить, что при компиляции через g++ следует использовать флаг -static , если программа работает со файлами и использует типы из заголовочного файла fstream:
Урок №212. Базовый файловый ввод и вывод
Работа файлового ввода/вывода в языке C++ почти аналогична работе обычных потоков ввода/вывода (но с небольшими нюансами).
Классы файлового ввода/вывода
Есть три основных класса файлового ввода/вывода в языке C++:
ofstream (является дочерним классу ostream);
fstream (является дочерним классу iostream ).
С помощью этих классов можно выполнить однонаправленный файловый ввод, однонаправленный файловый вывод и двунаправленный файловый ввод/вывод. Для их использования нужно всего лишь подключить заголовочный файл fstream.
В отличие от потоков cout, cin, cerr и clog, которые сразу же можно использовать, файловые потоки должны быть явно установлены программистом. То есть, чтобы открыть файл для чтения и/или записи, нужно создать объект соответствующего класса файлового ввода/вывода, указав имя файла в качестве параметра. Затем, с помощью оператора вставки ( << ) или оператора извлечения ( >> ), можно записывать данные в файл или считывать содержимое файла. После проделывания данных действий нужно закрыть файл — явно вызвать метод close() или просто позволить файловой переменной ввода/вывода выйти из области видимости (деструктор файлового класса ввода/вывода закроет этот файл автоматически вместо нас).
Файловый вывод
Для записи в файл используется класс ofstream . Например:
Если вы загляните в каталог вашего проекта (ПКМ по вкладке с названием вашего файла .cpp в Visual Studio > «Открыть содержащую папку»), то увидите файл с именем SomeText.txt, в котором находятся следующие строки:
See line #1!
See line #2!
Обратите внимание, мы также можем использовать метод put() для записи одного символа в файл.
Файловый ввод
Теперь мы попытаемся прочитать содержимое файла, который создали в предыдущем примере. Обратите внимание, ifstream возвратит 0 , если мы достигли конца файла (это удобно для определения «длины» содержимого файла). Например:
Результат выполнения программы:
Хм, это не совсем то, что мы хотели. Как мы уже узнали на предыдущих уроках, оператор извлечения работает с «отформатированными данными», т.е. он игнорирует все пробелы, символы табуляции и символ новой строки. Чтобы прочитать всё содержимое как есть, без его разбивки на части (как в примере, приведенном выше), нам нужно использовать метод getline():
Результат выполнения программы:
See line #1!
See line #2!
Буферизованный вывод
Вывод в языке C++ может быть буферизован. Это означает, что всё, что выводится в файловый поток, не может сразу же быть записанным на диск (в конкретный файл). Это сделано, в первую очередь, по соображениям производительности. Когда данные буфера записываются на диск, то это называется очисткой буфера. Одним из способов очистки буфера является закрытие файла. В таком случае всё содержимое буфера будет перемещено на диск, а затем файл будет закрыт.
Буферизация вывода обычно не является проблемой, но при определенных обстоятельствах она может вызвать проблемы у неосторожных новичков. Например, когда в буфере хранятся данные, а программа преждевременно завершает свое выполнение (либо в результате сбоя, либо путем вызова функции exit()). В таких случаях деструкторы классов файлового ввода/вывода не выполняются, файлы никогда не закрываются, буферы не очищаются и наши данные теряются навсегда. Вот почему хорошей идеей является явное закрытие всех открытых файлов перед вызовом функции exit().
Также буфер можно очистить вручную, используя метод ostream::flush() или отправив std::flush в выходной поток. Любой из этих способов может быть полезен для обеспечения немедленной записи содержимого буфера на диск в случае сбоя программы.
Интересный нюанс: Поскольку std::endl; также очищает выходной поток, то его чрезмерное использование (приводящее к ненужным очисткам буфера) может повлиять на производительность программы (так как очистка буфера в некоторых случаях может быть затратной операцией). По этой причине программисты, которые заботятся о производительности своего кода, часто используют \n вместо std::endl для вставки символа новой строки в выходной поток, дабы избежать ненужной очистки буфера.
Режимы открытия файлов
Что произойдет, если мы попытаемся записать данные в уже существующий файл? Повторный запуск вышеприведенной программы (самая первая) показывает, что исходный файл полностью перезаписывается при повторном запуске программы. А что, если нам нужно добавить данные в конец файла? Оказывается, конструкторы файлового потока принимают необязательный второй параметр, который позволяет указать программисту способ открытия файла. В качестве этого параметра можно передавать следующие флаги (которые находятся в классе ios ):
app — открывает файл в режиме добавления;
ate — переходит в конец файла перед чтением/записью;
binary — открывает файл в бинарном режиме (вместо текстового режима);
in — открывает файл в режиме чтения (по умолчанию для ifstream );
out — открывает файл в режиме записи (по умолчанию для ofstream );
trunc — удаляет файл, если он уже существует.
Можно указать сразу несколько флагов путем использования побитового ИЛИ (|).
ifstream по умолчанию работает в режиме ios::in;
ofstream по умолчанию работает в режиме ios::out;
fstream по умолчанию работает в режиме ios::in ИЛИ ios::out, что означает, что вы можете выполнять как чтение содержимого файла, так и запись данных в файл.
Работа с файлами в С++
Большинство компьютерных программ работают с файлами, и поэтому возникает необходимость создавать, удалять, записывать читать, открывать файлы. Что же такое файл? Файл – именованный набор байтов, который может быть сохранен на некотором накопителе. Ну, теперь ясно, что под файлом понимается некоторая последовательность байтов, которая имеет своё, уникальное имя, например файл.txt . В одной директории не могут находиться файлы с одинаковыми именами. Под именем файла понимается не только его название, но и расширение, например: file.txt и file.dat — разные файлы, хоть и имеют одинаковые названия. Существует такое понятие, как полное имя файлов – это полный адрес к директории файла с указанием имени файла, например: D:\docs\file.txt . Важно понимать эти базовые понятия, иначе сложно будет работать с файлами.
Для работы с файлами необходимо подключить заголовочный файл <fstream> . В <fstream> определены несколько классов и подключены заголовочные файлы <ifstream> — файловый ввод и <ofstream> — файловый вывод.
Файловый ввод/вывод аналогичен стандартному вводу/выводу, единственное отличие – это то, что ввод/вывод выполнятся не на экран, а в файл. Если ввод/вывод на стандартные устройства выполняется с помощью объектов cin и cout , то для организации файлового ввода/вывода достаточно создать собственные объекты, которые можно использовать аналогично операторам cin и cout .
Например, необходимо создать текстовый файл и записать в него строку Работа с файлами в С++ . Для этого необходимо проделать следующие шаги:
- создать объект класса ofstream ;
- связать объект класса с файлом, в который будет производиться запись;
- записать строку в файл;
- закрыть файл.
Почему необходимо создавать объект класса ofstream , а не класса ifstream ? Потому, что нужно сделать запись в файл, а если бы нужно было считать данные из файла, то создавался бы объект класса ifstream .
Назовём объект – fout , Вот что получится:
Для чего нам объект? Объект необходим, чтобы можно было выполнять запись в файл. Уже объект создан, но не связан с файлом, в который нужно записать строку.
Через операцию точка получаем доступ к методу класса open(), в круглых скобочках которого указываем имя файла. Указанный файл будет создан в текущей директории с программой. Если файл с таким именем существует, то существующий файл будет заменен новым. Итак, файл открыт, осталось записать в него нужную строку. Делается это так:
Используя операцию передачи в поток совместно с объектом fout строка Работа с файлами в С++ записывается в файл. Так как больше нет необходимости изменять содержимое файла, его нужно закрыть, то есть отделить объект от файла.
Итог – создан файл со строкой Работа с файлами в С++ .
Шаги 1 и 2 можно объединить, то есть в одной строке создать объект и связать его с файлом. Делается это так:
Объединим весь код и получим следующую программу.
Осталось проверить правильность работы программы, а для этого открываем файл cppstudio.txt и смотрим его содержимое, должно быть — Работа с файлами в С++ .
Для того чтобы прочитать файл понадобится выполнить те же шаги, что и при записи в файл с небольшими изменениями:
- создать объект класса ifstream и связать его с файлом, из которого будет производиться считывание;
- прочитать файл;
- закрыть файл.
В программе показаны два способа чтения из файла, первый – используя операцию передачи в поток, второй – используя функцию getline() . В первом случае считывается только первое слово, а во втором случае считывается строка, длинной 50 символов. Но так как в файле осталось меньше 50 символов, то считываются символы включительно до последнего. Обратите внимание на то, что считывание во второй раз ( строка 17 ) продолжилось, после первого слова, а не с начала, так как первое слово было прочитано в строке 14 . Результат работы программы показан на рисунке 1.
Рисунок 1 — Работа с файлами в С++
Программа сработала правильно, но не всегда так бывает, даже в том случае, если с кодом всё впорядке. Например, в программу передано имя несуществующего файла или в имени допущена ошибка. Что тогда? В этом случае ничего не произойдёт вообще. Файл не будет найден, а значит и прочитать его не возможно. Поэтому компилятор проигнорирует строки, где выполняется работа с файлом. В результате корректно завершится работа программы, но ничего, на экране показано не будет. Казалось бы это вполне нормальная реакции на такую ситуацию. Но простому пользователю не будет понятно, в чём дело и почему на экране не появилась строка из файла. Так вот, чтобы всё было предельно понятно в С++ предусмотрена такая функция — is_open() , которая возвращает целые значения: 1 — если файл был успешно открыт, 0 — если файл открыт не был. Доработаем программу с открытием файла, таким образом, что если файл не открыт выводилось соответствующее сообщение.
Результат работы программы показан на рисунке 2.
Рисунок 2 — Работа с файлами в С++
Как видно из рисунка 2 программа сообщила о невозможности открыть файл. Поэтому, если программа работает с файлами, рекомендуется использовать эту функцию, is_open() , даже, если уверены, что файл существует.
Режимы открытия файлов
Режимы открытия файлов устанавливают характер использования файлов. Для установки режима в классе ios_base предусмотрены константы, которые определяют режим открытия файлов (см. Таблица 1).
| Константа | Описание |
|---|---|
| ios_base::in | открыть файл для чтения |
| ios_base::out | открыть файл для записи |
| ios_base::ate | при открытии переместить указатель в конец файла |
| ios_base::app | открыть файл для записи в конец файла |
| ios_base::trunc | удалить содержимое файла, если он существует |
| ios_base::binary | открытие файла в двоичном режиме |
Режимы открытия файлов можно устанавливать непосредственно при создании объекта или при вызове функции open() .
Режимы открытия файлов можно комбинировать с помощью поразрядной логической операции или | , например: ios_base::out | ios_base::trunc — открытие файла для записи, предварительно очистив его.
Объекты класса ofstream , при связке с файлами по умолчанию содержат режимы открытия файлов ios_base::out | ios_base::trunc . То есть файл будет создан, если не существует. Если же файл существует, то его содержимое будет удалено, а сам файл будет готов к записи. Объекты класса ifstream связываясь с файлом, имеют по умолчанию режим открытия файла ios_base::in — файл открыт только для чтения. Режим открытия файла ещё называют — флаг, для удобочитаемости в дальнейшем будем использовать именно этот термин. В таблице 1 перечислены далеко не все флаги, но для начала этих должно хватить.
Обратите внимание на то, что флаги ate и app по описанию очень похожи, они оба перемещают указатель в конец файла, но флаг app позволяет производить запись, только в конец файла, а флаг ate просто переставляет флаг в конец файла и не ограничивает места записи.
Разработаем программу, которая, используя операцию sizeof() , будет вычислять характеристики основных типов данных в С++ и записывать их в файл. Характеристики:
- число байт, отводимое под тип данных
- максимальное значение, которое может хранить определённый тип данных.
Запись в файл должна выполняться в таком формате:
Такая программа уже разрабатывалась ранее в разделе Типы данных С++ , но там вся информация о типах данных выводилась на стандартное устройство вывода, а нам необходимо программу переделать так, чтобы информация записывалась в файл. Для этого необходимо открыть файл в режиме записи, с предварительным усечением текущей информации файла (строка 14). Как только файл создан и успешно открыт (строки 16 — 20), вместо оператора cout , в строке 22 используем объект fout . таким образом, вместо экрана информация о типах данных запишется в файл.
Нельзя не заметить, что изменения в программе минимальны, а всё благодаря тому, что стандартный ввод/вывод и файловый ввод/вывод используются абсолютно аналогично. В конце программы, в строке 45 мы явно закрыли файл, хотя это и не обязательно, но считается хорошим тоном программирования. Стоит отметить, что все функции и манипуляторы используемые для форматирования стандартного ввода/вывода актуальны и для файлового ввода/вывода. Поэтому не возникло никаких ошибок, когда оператор cout был заменён объектом fout .
23.6 – Основы файлового ввода/вывода
Файловый ввод/вывод в C++ работает очень похоже на обычный ввод/вывод (с небольшими дополнительными сложностями). В C++ есть 3 основных класса файлового ввода/вывода: ifstream (производный от istream ), ofstream (производный от ostream ) и fstream (производный от iostream ). Эти классы выполняют файловый ввод, вывод и ввод/вывод соответственно. Чтобы использовать классы файлового ввода/вывода, вам необходимо включить заголовок fstream .
В отличие от потоков cout , cin , cerr и clog , которые уже готовы к использованию, файловые потоки должны быть настроены программистом явно. Однако это очень просто: чтобы открыть файл для чтения и/или записи, просто создайте экземпляр объекта соответствующего класса файлового ввода/вывода с именем файла в качестве параметра. Затем используйте операторы вставки и извлечения для записи или чтения данных из файла. Как только вы закончите, есть несколько способов закрыть файл: явно вызвать функцию close() или просто позволить переменной ввода/вывода файла выйти за пределы области видимости (деструктор класса файлового ввода/вывода закроет файл за вас).
Вывод в файл
Для вывода в файл в следующем примере мы собираемся использовать класс ofstream . Это очень просто:
Если вы заглянете в каталог своего проекта, вы должны увидеть файл с именем Sample.dat . Если вы откроете его в текстовом редакторе, то увидите, что он действительно содержит две строки, которые мы записали в файл.
Обратите внимание, что для записи одного символа в файл можно использовать функцию put() .
Ввод из файла
Теперь возьмем файл, который мы написали в предыдущем примере, и прочитаем его с диска. Обратите внимание, что ifstream возвращает 0, если мы достигли конца файла (EOF, «end of the flile»). Мы воспользуемся этим фактом, чтобы определить, как долго нужно читать.
Эта программа дает следующий результат:
Хм, это было не совсем то, что мы хотели. Помните, что оператор извлечения разбивает входные строки пробелами. Чтобы читать строки полностью, нам нужно использовать функцию getline() .
Эта программа дает следующий результат:
Буферизованный вывод
Вывод в C++ может буферизоваться. Это означает, что всё, что выводится в файловый поток, может сразу не записываться на диск. Вместо этого несколько операций вывода могут быть объединены и обработаны вместе. Это сделано в первую очередь из соображений производительности. Когда буфер записывается на диск, это называется очисткой/сбросом буфера (англоязычный термин «flushing»). Один из способов вызвать сброс буфера – закрыть файл: содержимое буфера будет сброшено на диск, а затем файл будет закрыт.
Буферизация обычно не проблема, но в определенных обстоятельствах при неосторожности она может вызвать сложности. Главная проблема в этом случае – когда в буфере есть данные, а программа немедленно завершается (либо из-за сбоя, либо из-за вызова exit() ). В этих случаях деструкторы классов файловых потоков не выполняются, что означает, что файлы не закрываются, что означает, что буферы не сбрасываются. В этом случае данные в буфере не записываются на диск и теряются навсегда. Вот почему всегда рекомендуется явно закрывать все открытые файлы перед вызовом exit() .
Можно очистить буфер вручную, используя функцию ostream::flush() или отправив std::flush в выходной поток. Любой из этих методов может быть полезен для обеспечения немедленной записи содержимого буфера на диск на случай сбоя программы.
Одно интересное замечание: std::endl ; также очищает выходной поток. Следовательно, чрезмерное использование std::endl (вызывающее ненужную очистку буфера) может повлиять на производительность при выполнении буферизованного ввода/вывода, когда операции очистки дороги (например, запись в файл). По этой причине программисты, заботящиеся о производительности, для вставки новой строки в выходной поток часто используют ' \n ' вместо std::endl , чтобы избежать ненужной очистки буфера.
Режимы открытия файлов
Что произойдет, если мы попытаемся записать данные в уже существующий файл? Повторный запуск примера вывода в файл показывает, что при каждом запуске программы исходный файл полностью перезаписывается. Что, если вместо этого мы захотим добавить еще немного данных в конец файла? Оказывается, конструкторы файловых потоков принимают необязательный второй параметр, который позволяет указать, как следует открывать файл. Этот параметр называется режимом, и допустимые флаги, которые он принимает, находятся в классе ios .
| Режим открытия файла ios | Значение |
|---|---|
| app | Открывает файл в режиме добавления |
| ate | Ищет конец файла перед чтением/записью |
| binary | Открывает файл в двоичном режиме (вместо текстового режима) |
| in | Открывает файл в режиме чтения (по умолчанию для ifstream ) |
| out | Открывает файл в режиме записи (по умолчанию для ofstream ) |
| trunc | Стирает файл, если он уже существует |
Можно указать несколько флагов путем их объединения с помощью побитового ИЛИ (с помощью оператора | ). ifstream по умолчанию использует режим std::ios::in . ofstream по умолчанию использует режим std::ios::out . fstream по умолчанию имеет режим std::ios::in | std::ios::out , то есть по умолчанию вы можете и читать, и писать.
Совет
Из-за того, как fstream был разработан, он может дать сбой, если используется std::ios::in , и открываемый файл не существует. Если вам нужно создать новый файл с помощью fstream , используйте режим только std::ios::out .
Давайте напишем программу, которая добавляет еще две строки к ранее созданному файлу Sample.dat :
Теперь, если мы взглянем на Sample.dat (используя одну из приведенных выше программ-примеров, которая печатает его содержимое, или используя текстовый редактор), мы увидим следующее:
Явное открытие файлов с помощью open()
Также файловый поток можно явно открыть с помощью open() и явно закрыть его с помощью close() . open() работает так же, как конструкторы файловых потоков – она принимает имя файла и необязательный параметр режима открытия файла.