Help — Ввод и вывод бинарным файлом на Си


Содержание

Help — Ввод и вывод бинарным файлом на Си

Блог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()) по причинам, которые обсуждались в предыдущей лекции.

В следующем примере показан цикл считывания строк из файла test.txt и их отображения на консоли.

Этот код под ОС Windows также зависит от наличия в последней строке файла символа перевода строки, надежнее было бы сделать так:

Явные вызовы методов open и close не обязательны. Действительно, вызов конструктора с аргументом позволяет сразу же, в момент создания поточного объекта file , открыть файл:

Вместо метода close можно использовать оператор delete , который автоматически вызовет деструктор объекта file и закроет файл. Код цикла while обеспечивает надлежащую проверку признака конца файла.

Класс ofstream: запись файлов

Класс ofstream предназначен для вывода данных из файлового потока. Далее перечислены основные методы данного класса.

Метод Описание
open Открывает файл для записи
put Записывает одиночный символ в файл
write Записывает заданное число байт из памяти в файл
seekp Перемещает указатель позиционирования в указанное положение
tellp Возвращает текущее значение указателя позиционирования файла
close Закрывает файл

Описанный ранее оператор включения удобен для организации записи в текстовый файл:

В принципе, бинарные данные обслуживаются наподобие текстовых. Отличие состоит в том, что если бинарные данные записываются в определенной логической структуре, то они должны считываться из файла в переменную того же структурного типа.

Первый параметр методов write и read (адрес блока записи/чтения) должен иметь тип символьного указателя char * , поэтому необходимо произвести явное преобразование типа адреса структуры void * . Второй параметр указывает, что бинарные блоки файла имеют постоянный размер байтов независимо от фактической длины записи. Следующее приложение дает пример создания и отображения данных простейшей записной книжки. Затем записи файла последовательно считываются и отображаются на консоли.

В результате выполнения этого кода образуется бинарный файл Notebook.dat из трех блоков размером по 80 байт каждый (при условии, что символы — однобайтовые). Естественно, вы можете использовать другие поточные методы и проделывать любые операции над полями определенной структуры данных.

Класс fstream: произвольный доступ к файлу

Предположим что в нашей записной книжке накопилось 100 записей, а мы хотим считать 50-ю. Конечно, можно организовать цикл и прочитать все записи с первой по заданную. Очевидно, что более целенаправленное решение — установить указатель позиционирования файла pos прямо на запись 50 и считать ее:

Подобные операции поиска эффективны, если файл состоит из записей известного и постоянного размера. Чтобы заменить содержимое произвольной записи, надо открыть поток вывода в режиме модификации:

Если не указать флаг ios::ate (или ios::app ), то при открытии бинарного файла Notebook.dat его предыдущее содержимое будет стерто!

Наконец, можно открыть файл одновременно для чтения/записи, используя методы, унаследованные поточным классом fstream от своих предшественников. Поскольку класс fstream произведен от istream и ostream (родителей ifstream и ofstream соответственно), все упомянутые ранее методы становятся доступными в приложении.

В следующем примере показана перестановка первой и третьей записей файла Notebook.dat .

В конструкторе объекта file надо указать флаги ios::in и ios::out , разрешая одновременное выполнение операций чтения и записи. В результате выполнения этого кода первая и третья записи бинарного файла Notebook.dat поменяются местами.

Дополнительные примеры по теме есть в этой заметке.

05.11.2015, 09:45; рейтинг: 103091

Двоичный ввод/вывод

Имеется несколько способов для записи двоичных данных в файл и чтения из файла. В этом разделе мы рассмотрим два из них. В первую очередь, можно записать байт с помощью функции-члена put() и прочитать байт, используя функцию-член get(). Функция get() имеет много форм, но наи­более употребительна показанная ниже версия, где приведена также функция put():

Функция get() читает единственный символ из ассоциированного потока и помещает его значе­ние в ch. Она возвращает ссылку на поток. Функция put() пишет ch в поток и возвращает ссылку на этот поток.

ЗАМЕТКА: Работая с двоичными файлами, надо удостовериться, что они от­крыты с использованием спецификатора ios::binary.

Следующая программа выводит содержимое любого файла на экран. Она использует функцию get().

#include
#include
int main(int argc, char *argv[])
<
char ch;
if (argc!=2) <
cout \n»;
return 1;
>
ifstream in(argv[1], ios::in | ios::binary);
if (!in) <
cout
#include
int main()
<
char *p = «hello there\n\r\xfe\xff»;
ofstream out(«test», ios::out | ios::binary );
if (!out) <
cout
#include
int main()
<
int n [5] = <1, 2, 3, 4, 5>;
register int i;
ofstream out («test», ios::out | ios::binary);
if (!out) <
cout

Работа с файлами в си ввод и вывод в файл в си

Работа с файлами в си

В этой статье мы узнаем, как считывать данные из файлов и записывать информацию в файлы в программах си. Файлы в си используются для того, чтобы сохранять результат работы программы си и использовать его при новом запуске программы . Например можно сохранять результаты вычислений , статистику игр.
Чтобы работать с файлами в си необходимо подключить библиотеку stdio.h
#include
Чтобы работать с файлом в си необходимо задать указатель на файл по образцу
FILE *имя указателя на файл;
Например
FILE *fin;
Задает указатель fin на файл
Дальше необходимо открыть файл и привязать его к файловому указателю. Для открытия файла в си на чтение используется команда
Имя указателя на файл= fopen(«путь к файлу», «r»);
Например следующая команда
fin = fopen(«C:\\Users\\user\\Desktop\\data.txt», «r»);
откроет файл data.txt, находящийся на рабочем столе по пути C:\\Users\\user\\Desktop Чтобы узнать путь к файлу можно выбрать файл мышью нажать на правую кнопку мыши и выбрать свойства файла. В разделе Расположение будет указан путь к файлу. Обратите внимание , что в си путь указывается с помощью двух слешей.
После работы с файлом в си , необходимо его закрыть с помощью команды
fclose(имя указателя на файл)

Считывание информации из текстового файла в Си

Чтобы можно было считывать русские символы из файла, необходимо настроить работу с Кириллицей с помощью команды
setlocale(LC_ALL, «Russian»);

При этом необходимо в начале программы подключить #include

Бинарный файл

Для работы с бинарными файлами в языке C++ используется стандартная библиотека для работы с файлами (см. функции для работы с текстовыми файлами fscanf и fprintf), но с небольшими изменениями.

Так, например, для чтения бинарного файла применяется параметр «rb», а не обычный «r». Аналогично и с модификаторами записи «w», добавления «a», чтения и записи «r+», «w+», «a+»: «wb», «ab», «rb+», «wb+», «ab+». Как и для обычных файлов наличие буквы w обозначает, что файл будет создан в случае отсутствия, а, если файл уже есть, то его содержимое будет удалено и заменено новым. А наличие буквы a значит, что файл будет создан в случае отсутствия, но содержимое файла не будет уничтожено.

Весь процесс идет по аналогии с обычным текстовым файлом, но есть некоторые нюансы. Работа идет именно с битами, так что возможна запись или считывание различных управляющих символов, например, “\n”, который заканчивает строку и начинает новую.

Так же еще одним моментом служит произвольный доступ к данным в бинарном файле. Благодаря функции

Файловый ввод / вывод в С (Си) 7306

Файловая система языка С состоит из нескольких взаимосвязанных функций. Самые распространенные из них показаны в таблице 4.1. Для их работы требуется заголовок .

Таблица 4.1. Часто используемые функции файловой системы С

Имя Что делает
fopen() Открывает файл
fclose() Закрывает файл
putc() Записывает символ в файл
fputc() То же, что и putc()
getc() Читает символ из файла
fgetc() То же, что и fgetc()
gets() Читает строку из файла
fputs() Записывает строку в файл
fseek() Устанавливает указатель текущей позиции на определенный байт файла
ftell() Возвращает текущее значение указателя текущей позиции файла
fprintf() Для файла то же, что и printf() для консоли
fscanf() Для файла то же, что и scanf() для консоли
feof() Возвращает значение true (истина), если достигнут конец файла
ferror() Возвращает значение true, если произошла ошибка
rewind() Устанавливает указатель текущей позиции в начало файла
remove() Стирает файл
fflush() Дозапись потока в файл

Указатель файла — это то, что соединяет в единое целое всю систему ввода/вывода языка С. Указатель файла — это указатель на структуру типа FILE. Он указывает на структуру, содержащую различные сведения о файле, например, его имя, статус и указатель текущей позиции в начало файла. В сущности, указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функций ввода/вывода. Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Чтобы объявить переменную-указатель файла, используйте такого рода оператор:

Функция fopen() открывает поток и связывает с этим потоком определенный файл. Затем она возвращает указатель этого файла. Чаще всего под файлом подразумевается дисковый файл. Прототип функции fopen() такой:

FILE *fopen(const char *имя_файла, const char *режим);

где имя_файла — это указатель на строку символов, представляющую собой допустимое имя файла, в которое также может входить спецификация пути к этому файлу. Строка, на которую указывает режим, определяет, каким образом файл будет открыт. В таблице 4.2 показано, какие значения строки режим являются допустимыми.

Таблица 4.2. Допустимые значения режим

Режим Что означает
r Открыть текстовый файл для чтения
w Создать текстовый файл для записи
a Добавить в конец текстового файла
rb Открыть двоичный файл для чтения
wb Создать двоичный файл для записи
ab Добавить в конец двоичного файла
r+ Открыть текстовый файл для чтения/записи
w+ Создать текстовый файл для чтения/записи
a+ Добавить в конец текстового файла или создать текстовый файл для чтения/записи
r+b Открыть двоичный файл для чтения/записи
w+b Создать двоичный файл для чтения/записи
a+b Добавить в конец двоичного файла или создать двоичный файл для чтения/записи

Строки, подобные «r+b» могут быть представлены и в виде «rb+». Как уже упоминалось, функция fopen() возвращает указатель файла. Никогда не следует изменять значение этого указателя в программе. Если при открытии файла происходит ошибка, то fopen() возвращает пустой (null) указатель.

В следующем коде (листинг 4.1) функция fopen() используется для открытия файла по имени TEST для записи.

fp = fopen(«test.txt», «w»);

Откомпилировав этот код, в папке с проектом в одноименной директории создается файл test.txt (рисунок 4.1). Это простой пока еще пустой текстовый документ, который открывается стандартным windows приложением Блокнот.

Рисунок 4.1. Создание текстового файла .txt

Можно указать другое расширение файла, например .doc. Откомпилировав код снова, в этой же папке появится пустой файл test.doc (рисунок 4.2)

Рисунок 4.2 Создание текстового файла .doc

Предыдущий код технически правильный, но его обычно пишут немного по-другому:

if ((fp = fopen(«test.txt», «w»)) == NULL)

printf(«Oshibka pri otkrytii faila!\n»);

Этот метод помогает при открытии файла обнаружить любую ошибку, например, защиту от записи или полный диск, причем обнаружить еще до того, как программа попытается в этот файл что-либо записать. Всегда нужно вначале получить подтверждение, что функция — fopen() выполнилась успешно, и лишь затем выполнять с файлом другие операции.

Название большинства файловых режимов объясняет их смысл, однако следует сделать некоторые дополнения. Если попытаться открыть файл только для чтения, а он не существует, то работа fopen() завершится отказом. А если попытаться открыть файл в режиме дозаписи, а сам этот файл не существует, то он просто будет создан. Более того, если файл открыт в режиме дозаписи, то все новые данные, которые записываются в него, будут добавляться в конец файла. Содержимое, которое хранилось в нем до открытия (если только оно было), изменено не будет. Далее, если файл открывают для записи, но выясняется, что он не существует, то он будет создан. А если он существует, то содержимое, которое хранилось в нем до открытия, будет утеряно, причем будет создан новый файл. Разница между режимами r+ и w+ состоит в том, что если файл не существует, то в режиме открытия r+ он создан не будет, а в режиме w+ все произойдет наоборот: файл будет создан. Более того, если файл уже существует, то открытие его в режиме w+ приведет к утрате его содержимого, а в режиме r+ оно останется нетронутым.

Из таблицы 4.2 видно, что файл можно открыть либо в одном из текстовых, либо в одном из двоичных режимов. В большинстве реализаций в текстовых режимах каждая комбинация кодов возврата каретки (ASCII 13) и конца строки (ASCII 10) преобразуется при вводе в символ новой строки. При выводе же происходит обратный процесс: символы новой строки преобразуются в комбинацию кодов возврата каретки (ASCII 13) и конца строки (ASCII 10). В двоичных режимах такие преобразования не выполняются.

Максимальное число одновременно открытых файлов определяется FOPEN_MAX. Это значение не меньше 8, но чему оно точно равняется — это должно быть написано в документации по компилятору. Это можно легко проверить, добавив в код вывод числа FOPEN_MAX. Пример представлен в листинге 4.3, а результат проиллюстрирован на рисунке 4.3.

if ((fp = fopen(«test.txt», «w»)) == NULL)

printf(«Oshibka pri otkrytii faila!\n»);

printf(«Maksimalnoe chislo otkrytyh failov = %d\n», FOPEN_MAX);

Рисунок 4.3. Результат работы листинга 4.3

Функция fclose() закрывает поток, который был открыт с помощью вызова fopen().Функция fclose() записывает в файл все данные, которые еще оставались в дисковом буфере, и проводит закрытие файла на уровне операционной системы. Отказ при закрытии потока влечет всевозможные неприятности, включая потерю данных, испорченные файлы и возможные периодические ошибки в программе. Функция fclose() также освобождает блок управления файлом, связанный с этим потоком, давая возможность использовать этот блок снова. Так как количество одновременно открытых файлов ограничено, то, возможно, придется закрывать один файл, прежде чем открывать другой. Прототип функции fclose() такой:

int fclose(FILE *уф);

где уф — указатель файла, возвращенный в результате вызова fopen(). Возвращение нуля означает успешную операцию закрытия. В случае же ошибки возвращается EOF. Чтобы точно узнать, в чем причина этой ошибки, можно использовать стандартную функцию ferror(). Обычно отказ при выполнении fclose() происходит только тогда, когда диск был преждевременно удален или на диске не осталось свободного места.

В системе ввода/вывода языка С определяются две эквивалентные функции, предназначенные для вывода символов: putc() и fputc(). Две идентичные функции имеются просто потому, чтобы сохранять совместимость со старыми версиями С. Функция putc() записывает символы в файл, который с помощью fopen() уже открыт в режиме записи. Прототип этой функции следующий:

int putc(int ch, FILE *уф);

Цукерберг рекомендует:  Интервью с Олегом Дорожком, руководителем маркетинговых коммуникаций Ozon.ru

где уф — это указатель файла, возвращенный функцией fopen(), a ch — выводимый символ. Указатель файла сообщает putc(), в какой именно файл следует записывать символ. Хотя ch и определяется как int, однако записывается только младший байт. Если функция putc() выполнилась успешно, то возвращается записанный символ. В противном же случае возвращается EOF.

Для ввода символа также имеются две эквивалентные функции: getc() и fgetc(). Обе определяются для сохранения совместимости со старыми версиями С. Функция getc() записывает символы в файл, который с помощью fopen() уже открыт в режиме для чтения. Прототип этой функции следующий:

int getc(FILE *уф);

где уф — это указатель файла, имеющий тип FILE и возвращенный функцией fopen(). Функция getc() возвращает целое значение, но символ находится в младшем байте. Если не произошла ошибка, то старший байт (байты) будет обнулен.

Если достигнут конец файла, то функция getc() возвращает EOF. Поэтому, чтобы прочитать символы до конца текстового файла, можно использовать следующий код;

Однако getc() возвращает EOF и в случае ошибки. Для определения того, что же на самом деле произошло, можно использовать ferror().

Теперь, имея все полученные знания, попробуем написать программу с помощью функций fopen(), putc() и fclose(). Этого минимального набора функций достаточно для проведения операций с файлами. В листинге 4.4 представлен код, при выполнении которого символы считываются с клавиатуры и записываются в дисковый файл до тех пор, пока пользователь не введет знак доллара.

char ch; //объявление символьной переменной ch

printf(«Vvodite lubie simvoly, krome $ \n\n»);

if ((fp = fopen(«test.txt», «w»)) == NULL)

printf(«Oshibka pri otkrytii faila!\n»);

Результат работы программы представлен на рисунке 4.4

Рисунок 4.4. Результат работы листинга 4.4

Теперь напишем программу, которая сможет открыть уже созданный файл и вывести содержимое на экран (листинг 4.5)


char ch; //объявление символьной переменной ch

printf(«Vvedite nazvanie faila, soderjimoe kotorogo hotite otkryt`\n»);

if ((fp = fopen(name, «r»)) == NULL)

printf(«Oshibka pri otkrytii faila!\n»);

ch = getc(fp); //чтение одного символа

while (ch != EOF)//пока не достигли конца документа

putchar(ch); //выводим на экран

Результат работы программы представлен на рисунке 4.5

Рисунок 4.5. Результат работы листинга 4.5

Из рисунка 4.5 видно, что содержимое файла полностью вывелось на экран. При работе с файлами важно не забывать писать расширение открываемого файла, иначе программа укажет ошибку (рисунок 4.6).

Рисунок 4.6. Вывод ошибки при неверно заданном имени файла

4.2 Задание к лабораторной работе

1. Создать консольное приложение, которое создает текстовый файл определенного типа и записывает текстовую информацию, введенную с клавиатуры в файл. Окончание записи в файл активировать с помощью спецсимвола. Текстовый файл должен содержать не менее 5 строчек. Варианты типов файлов и спецсимволов представлены в таблице 4.3

2. Создать второе консольное приложение, которое сможет дозаписывать текстовую информацию в файл, созданный в пункте 1.

3. * В созданных приложениях подключить возможность написание текста на кириллице.

Задание под знаком * дает возможность заработать дополнительные рейтинговые баллы, но не является обязательным для выполнения.

Таблица 4.3 Варианты заданий

Тип файла Спецсимвол
.doc @
.txt %
.rtf *
.doc
.txt $
.rtf #
.doc ^
.txt +
.rtf =

4.3 Содержание отчета

1. Титульный лист.

3. Задание к лабораторной работе.

4. Блок-схема программ.

5. Листинг программ.

6. Результат работы программ (следует показать реальную работу программы, а в отчете должны быть скриншоты выполненной работы).

7.* Листинг программы с дополнительным заданием.

8.* Результат работы программы с дополнительным заданием.

C++ для начинающих работа с бинарными файлами

vo >main ()
<
system ( “CLS” ); //Очистка экрана
int y = 0 ; //
Y будем записывать в файл
int x = 0 ;
//X будем считывать из файла

cout “Y = “ ; cin >> y ; //Вводим число, которое нужно сохранить в файл
ofstream out ( “C://1.txt” , ios :: binary | ios :: out );
//Открываем файл в двоичном режиме для записи
out . write ((char*)& y ,sizeof y );
//Записываем в файл число y
out . close ();
//Закрываем файл

cout “x = “ x endl ; //Показываем X до его изменений

ifstream in ( “C://1.txt” , ios :: binary | ios :: in ); //Открываем файл в двоичном режиме только для чтения
in . read ((char*)& x ,sizeof x );
//Читаем оттуда информацию и запоминаем её в X
in . close ();
//Закрываем файл

cout “x = “ x endl ; //Показываем X после изменения
system( “PAUSE” );
>

====================
Уверен, что кого-то могут смутить страшные строчки этого небольшого кода. Не беспокойтесь, он не так страшен, каким может казаться.

Для записи в файл использована команда ofstream, для чтения из файла использована команда instream
У обеих команд внутри скобок одинаковая конструкция.
Если вы присмотритесь к тому, что внутри скобок, то заметите во время записи в файл и во время чтения из файла там изменилась только одна буква, точнее одна переменная, над которой проводится действие.
Во время записи в файл, мы записывали в файл значение y и вот этот самый y стоит в первом случае.
Во время чтения информации, мы считывали информацию в x и вот этот самый x стоит во втором случае.
Про режимы открытия файлов можно почитать тут
Пример работы с текстовым файлом можно почитать тут

Возможно вы уже читали, возможно вам говорили, возможно вы вообще не знали, но когда приходится записывать структуру или класс в файл, то внутри структуры может быть очень много информационных полей и при записи структуры в файл в текстовом режиме придется записывать каждый элемент структуры, а это отнимет много времени. Вот при записи структуры в бинарный файл можно записать всю структуру целиком и сразу.

Код C++ Запись объекта структуры в бинарный файл. Чтение объекта структуры из бинарного файла
============================
#include //Для работы с клавиатурой
#include
//Для работы с файлами
#include
//Для очистки экрана

//Наша структура
struct MyStruct
<
char * Name ;
int size ;
>;

vo >main ()
<
system ( “CLS” );
//Очистка экрана
MyStruct X , Y ;
//Создали два объекта соответствующие нашей структуре
//Например объект X имеет такие параметры
X . Name = “МЕДВЕД” ; //
X . size = 100 ; //

//Открываем файл для записи в бинарном режиме
ofstream out ( “C://2.txt” , ios :: binary | ios :: out );
out . write ((char*)& X ,sizeof X );
//Записываем объект X в открытый нами файл
out . close ();
//Закрываем открытый файл

//Открываем файл только для чтения, открываем в бинарном режиме
fstream in ( “C://2.txt” , ios :: binary | ios :: in );
in . read ((char*)& Y ,sizeof Y );
//Считываем информацию в объект Y
in . close ();
//Закрываем открытый файл

//Показываем объект Y по его составным частям
cout Y . Name “\n” ;
cout Y . size “\n” ;

Если посмотреть на первый пример, где записывалось число и этот, где структура, то при записи файла и чтения файла легко увидеть однообразие. Так вот и получается, что в бинарный файл можно записать любой объект такой короткой инструкцией.

Для тех кому интересен пример записи класса в бинарный файл и чтение класса из бинарного файла, то вот. Я думаю, что тех кого эта часть интересует уже должны быть знакомы с классами. Я выкладывал немного материала, но это намного дальше чем расположена эта тема.

Код C++ Запись объекта класса в бинарный файл. Чтение объекта класса из бинарного файла
======================
#include
#include
#include

>MyClass
<
int z ;
//Недоступен ничему кроме своего класса
public:
int x ;
int y ;
MyClass () < z = 100 ;>;
//Инициализация z с помощью конструктора
vo >showZ () < cout z endl ;>;
//Метод класса, отображает на экране значение z
>;

vo >main ()
<
system( “CLS” );
MyClass A , O ;
//Создаем два объекта

//Задаем параметры объекту A и записываем его в файл
A . x = 50 ;
A . y = 60 ;
O . x = 0 ; //Инициализируем параметры объекта О в нули. Будем читать его из файла
O . y = 0 ;

ofstream out ( “C://1.txt” , ios :: binary | ios :: out ); //Открыли для записи в бинарном режиме
out . write ((char*)& A ,sizeof A );
//Записали объект А в открытый файл
out . close ();
//Закрыли открытый файл

ifstream in ( “C://1.txt” , ios :: binary | ios :: in ); //Открыли для только для чтения в бинарном режиме
in . read ((char*)& O ,sizeof O );
//Считали информацию в объект О
in . close ();
//Закрыли открытый файл

cout O . x O . y endl ; //Вывели два доступных поля объекта О на экран
O . showZ (); //
С помощью метода класса отобразили z из объекта О на экране

=======================================================
in . read ((char*)& Y ,sizeof Y ); //Считываем информацию в объект Y
Узнаем адрес объекта Y и кладем в объект Y каждый новый прочитанный байт.
Инструктируем, что нам в объект Y нужно положить sizeof Y байт

=======================================================
Все статьи всего этого блога созданы и пока еще создаются одним человеком. Благодаря читателям в статьях исправляются ошибки и неточности .
Я выкладываю всё бесплатно и для того, чтоб вы могли разобраться. Но я трачу много сил и много времени. Если вы считаете нужным и цените мой труд, то можете отблагодарить материально

можно пополнить счет телефона
МТС (РОССИЯ) +7 (981) 857-10-21

или кошельки WebMoney
R375024497470
E149319127674
Z301246203264

Урок №212. Базовый файловый ввод/вывод

Обновл. 15 Июл 2020 |

Работа файлового ввода/вывода в 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, что означает, что вы можете выполнять как чтение содержимого файла, так и запись данных в файл.

Ввод и вывод данных средствами языка C

В системе ввода-вывода в Си для программ поддерживается единый интерфейс, не зависящий от того, к какому конкретному устройству осуществляется доступ. То есть в Си между программой и устройством находится нечто более общее, чем само устройство. Такое обобщенное устройство ввода или вывода (устройство более высокого уровня абстракции) называется потоком. В то же время конкретное устройство называется файлом. Наша задача — понять, каким обрзом происходит взаимодействие потоков и файлов.
Файловая система Си предназначена для работы с разными устройствами, в том числе с терминалами, дисководами и накопителями. Даже, если какое-то устройство очень сильно отличается от других устройств, буферизованная файловая система все равно представит его в виде логического устройства, которое называется потоком. Все потоки ведут себя похожим образом. И так как они в основном не зависят от физических устройств, то та же функция, которая выполняет запись в дисковый файл, может ту же операцию выполнить и на другом устройстве. Например, на консоли. Потоки бывают двух видов: текстовые и двоичные.
В языке Си файлом может быть все, что угодно, начиная в дискового файла и заканчивая терминалом или принтером. Поток связывают с определенным файлом, выполняя обязательную операцию открытия. Как только файл открыт, можно проводить обмен информацией между ним и программой.
Но не у всех файлов одинаковые возможности. Например, к дисковому файлу прямой доступ возможен, в то время как к некоторым принтерам — он не возможен. Таким образом, вы видите, что напрашивается определенный вывод, являющийся принципом системы ввода-вывода языка Си: все потоки одинаковы, а файлы — нет!
Если файл может поддерживать запросы на местоположение (указатель текущей позиции), то при открытии такого файла указатель текущей позиции в файле устанавливается в начало файла. При чтении каждого символа из файла (или записи в файл) указатель текущей позиции увеличивается. Тем самым обеспечивается продвижение по файлу.
Файл отсоединяется от определенного потока (то есть разрывается связь между файлом и потоком) с помощью операции закрытия файла. При закрытии файла, открытого с целью вывода, содержимое (если оно, конечно, есть) связанного с ним потока записывается на внешнее устройство. Этот процесс обычно называют дозаписью потока. При этом гарантируется, что никакая информация случайно не останется в буфере диска.
Если программа завершает работу нормально, то есть либо main() возвращает управление операционной системе, либо выход происходит через exit(), то все файлы закрываются автоматически.
В случае же аварийного завершения работы программы, например, в случа краха или завершения путем вызова abort(), файлы не закрываются.
У каждого потока, связанного с файлом, имеется управляющая структура, содержащая информацию о файле. Она имеет тип FILE. Блок управления файлом — это небольшой блок памяти, временно выделенный операционной системой для хранения информации о файле, который был открыт для использования. Блок управления файлом обычно содержит информацию об идентификаторе файла, его расположении на диске и указателе текущей позиции в файле.
Для выполнения всех операций ввода-вывода следует использовать только понятия потоков и применять всего лишь одну файловую систему. Ввод или вывод от каждого устройства автоматически преобразуется системой в легко управлемый поток. И это является достижением языка Си.
Таковы основополагающие замечания относительно существования различных потоков информации и связанных с ними файлов.
Файловая система языка Си состоит из нескольких взаимосвязанных между собой функций. Для их работы в Си требуется заголовочный файл и такой же аналогичный ему заголовочный файл требуется для работы в С++.
Ниже приведена таблица основных (часто используемых) функций файловой системы языка Си.

Имя Что делает эта функция Имя Что делает эта функция
fopen() Открывает файл feof() Возвращает значение true (истина), если достигнут конец файла
fclose() Закрывает файл ferror() Возвращает значение true (истина), если произошла ошибка
putc() Записывает символ в файл remove() Стирает файл
fputc() То же, что и putc() fflush() Дозапись потока в файл
getc() Читает символ из файла rewind() Устанавливает указатель текущей позиции в начало файла
fgetc() То же, что и getc() ftell() Возвращает текущее значение указателя текущей позиции в файле
fgets() Читает строку из файла fprintf() Для файла то же, что printf() для консоли
fputs() Записывает строку в файл fscanf() Для файла то же, что scanf() для консоли
fseek() Устанавливает указатель текущей позиции на определенный байт файла

Заголовок представляет прототипы функций ввода-вывода в Си и определяет следующие три типа: size_t, fpos_t и FILE. Первые два: size_t, fpos_t представляют собой разновидности такого типа, как целое без знака. Отдельно рассмотрим третий тип: FILE.
Указатель файла — это то, что соединяет в единое целое всю систему ввода-вывода языка Си. Указатель файла — это указатель на структуру типа FILE. Он указывает на структуру, содержащую различные сведения о файле, например, его имя, статус, и указатель текущей позиции в начало файла. В сущности указатель файла определяет конкретный файл и используется соответствующим потоком при выполнении функции ввода-вывода.
Чтобы выполнять в файлах операции чтения и записи, программы должны использовать указатели соответствующих файлов. Чтобы объвить переменную-указатель файла необходимо использовать следующий оператор:
FILE *fp;
Функция fopen() открывает поток и связывает с этим потоком файл. Затем она возвращает указатель этого файла. Прототип функции имеет вид:
FILE *fopen(const char *имя_файла, const char *режим);
Здесь имя_файла — это указатель на строку символов, представляющую собой допустимое имя файла, в которое может входить спецификация файла (включает обозначение логического устройства, путь к файлу и собственно имя файла).
Режим — определяет, каким образом файл будет открыт. Ниже в таблице показаны допустимые значения режимов.

Цукерберг рекомендует:  Вакансии АТФ (АйТиФорсез)
Режим Что обозначает данный режим
r Открыть текстовый файл для чтения
w Создать текстовый файл для записи
a Добавить в конец текстового файла
wb Создать двоичный файл для записи
rb Открыть двоичный файл для чтения
ab Добавить в конец двоичного файла
r+ Открыть текстовый файл для чтения/записи
w+ Создать текстовый файл для чтения/записи
a+ Добавить в конец текстового файла или создать текстовый файл для чтения/записи
r+b Открыть двоичный файл для чтения/записи
w+b Создать двоичный файл для чтения/записи
a+b Добавить в конец двоичного файла или создать двоичный файл для чтения/записи

Приведем фрагмент программы, в котором используется функция fopen() для открытия файла по имени TEST.
FILE *fp;
fp = fopen(«test», «w»);
Следует сразу же указать на недостаточность такого кода в программе. Хотя приведенный код технически правильный, но его обычно пишут немного по-другому.
FILE *fp;
if ((fp = fopen(«test», «w»)==NUL)
<
printf(«Ошибка при открытии файла.\n\r»)»
exit(1);
>

Рис. 1
Этот метод помогает при открытии файла обнаружить любую ошибку.
Например, защиту от записи или полный диск. Причем, обнаружить еще до того, как программа попытается в этот файл что-то записать. Поэтому всегда нужно вначале получить подтверждение, что функция fopen() выполнилась успешно, и лишь затем выполнять c файлом другие операции. Ниже на рисунке 1 приведена небольшую часть программы, которая. подтверждает или не подтверждает открытие файла. Результаты работы указанной программы приведены на рисунке 2.

Потоковый ввод-вывод
На уровне потокового ввода-вывода обмен данными производится побайтно. Такой ввод-вывод возможен как для собственно устройств побайтового обмена (печатающее устройство, дисплей), так и для файлов на диске, хотя устройства внешней памяти, строго говоря, являются устройствами поблочного обмена, т.е. за одно обращение к устройству производится считывание или запись фиксированной порции данных. Чаще всего минимальной порцией данных, участвующей в обмене с внешней памятью, являются блоки в 512 байт или 1024 байта. При вводе с диска (при чтении из файла) данные помещаются в буфер операционной системы, а затем побайтно или определенными порциями передаются программе пользователя. При выводе данных в файл они накапливаются в буфере, а при заполнении буфера записываются в виде единого блока на диск за одно обращение к последнему. Буферы операционной системы реализуются в виде участков основной памяти. Поэтому пересылки между буферами ввода-вывода и выполняемой программой происходят достаточно быстро в отличие от реальных обменов с физическими устройствами.
Функции библиотеки ввода-вывода языка Си, поддерживающие обмен данными с файлами на уровне потока, позволяют обрабатывать данные различных размеров и форматов, обеспечивая при этом буферизованный ввод и вывод. Таким образом, поток — это файл вместе с предоставляемыми средствами буферизации.
При работе с потоком можно производить следующие действия:
· открывать и закрывать потоки (связывать указатели на потоки с конкретными файлами);
· вводить и выводить: символ, строку, форматированные данные, порцию данных произвольной длины;
· анализировать ошибки потокового ввода-вывода и условие достижения конца потока (конца файла);
· управлять буферизацией потока и размером буфера;
· получать и устанавливать указатель (индикатор) текущей позиции
При открытии потока могут возникнуть следующие ошибки: указанный файл, связанный с потоком, не найден (для режима «чтение»); диск заполнен или диск защищен от записи и т.п. Необходимо также отметить, что при выполнении функции fopen() происходит выделение динамической памяти. При её отсутствии устанавливается признак ошибки «Not enough memory» (недостаточно памяти). В перечисленных случаях указатель на поток приобретает значение NULL. Заметим, что указатель на поток в любом режиме, отличном от аварийного никогда не бывает равным NULL.
Приведем типичную последовательность операторов, которая используется при открытии файла, связанного с потоком:
if ((fp = fopen(«t.txt»,»w»)) == NULL)
perror(«ошибка при открытии файла t.txt \n»);
exit(0);
>
Где NULL — нулевой указатель, определенный в файле stdio.h.
Открытые на диске файлы после окончания работы с ними рекомендуется закрыть явно. Для этого используется библиотечная функция
int fclose (указатель_на_поток);
Открытый файл можно открыть повторно (например, для изменения режима работы с ним) только после того, как файл будет закрыт с помощью функции fclose().
Когда программа начинает выполняться, автоматически открываются пять потоков, из которых основными являются:
· стандартный поток ввода (на него ссылаются, используя предопределенный указатель на поток stdin);
· стандартный поток вывода (stdout);
· стандартный поток вывода сообщений об ошибках (stderr).
По умолчанию стандартному потоку ввода stdin ставится в соответствие клавиатура, а потокам stdout и stderr соответствует экран дисплея.
Одним из наиболее эффективных способов осуществления ввода-вывода одного символа является использование библиотечных функций getchar( ) и putchar(). Прототипы этих функций имеют следующий вид:
int getchar(void);
int putchar(int c);
Функция getchаr( ) осуществляет ввод одного символа. При обращении она возвращает в вызвавшую ее функцию один введенный символ.
Функция putchar( ) выводит в стандартный поток один символ, при этом также возвращает в вызвавшую ее функцию только что выведенный символ.
Обратите внимание на то, что функция getchar( ) вводит очередной байт информации (символ) в виде значения типа int. Это сделано для того, чтобы гарантировать успешность распознавания ситуации «достигнут конец файла». Дело в том, что при чтении из файла с помощью функции getchar() может быть достигнут конец файла. В этом случае операционная система в ответ на попытку чтения символа передает функции getchar() значение EOF (End of File). Константа EOF определена в заголовочном файле stdio.h и в разных операционных системах имеет значение 0 или -1. Таким образом, функция getchar() должна иметь возможность прочитать из входного потока не только символ, но и целое значение. Именно с этой целью функция getchar( ) всегда возвращает значение типа int.
В случае ошибки при вводе функция getchar() также возвращает EOF.
При наборе текста на клавиатуре коды символов записываются во внутренний буфер операционной системы, Одновременно они отображаются (для визуального контроля) на экране дисплея. Набранные на клавиатуре символы можно редактировать (удалять и набирать новые). Фактический перенос символов из внутреннего буфера в программу происходит при нажатии клавиши . При этом код клавиши также заносится во внутренний буфер. Таким образом, при нажатии на (Клавишу ‘А’ и клавишу (завершение ввода) во внутреннем буфере оказываются: код символа ‘А’ и код клавиши . ) Об этом необходимо помнить, если вы рассчитываете на ввод функцией getchar() одиночного символа.
Приведём в пример программу копирования из стандартного ввода в стандартный вывод:
#include
int main()
<
int c;
while ((c=getchar())!=EOF)
Putchar(c);
return 0;
>
Для завершения приведенной выше программы копирования необходимо ввести с клавиатуры сигнал прерывания Ctrl+C.
Одной из наиболее популярных операций ввода-вывода является операция ввода-вывода строки символов. В библиотеку языка Си для обмена данными через Стандартные потоки ввода-вывода включены функции ввода-вывода строк gets() и puts(), которые удобно использовать при создании диалоговых систем. Прототипы этих функций имеют следующий вид:
char * gets (char * s); /* Функция ввода */
int puts (char * s); /* Функция вывода */
Обе функции имеют только один аргумент — указатель s на массив символов. Бели строка прочитана удачно, функция gets( ) возвращает адрес того массива s, в который производился ввод строки. Если произошла ошибка, то возвращается NULL.
Функция puts() в случае успешного завершения возвращает последний выведенный символ, который всегда является символом ‘\n’. Если произошла ошибка, то возвращается EOF.

32) Ввод и вывод данных средствами языка C++.

В языке С имеется весьма развитая библиотека функций ввода-вывода. Однако в самом языке отсутствуют какие-либо предопределенные файловые структуры. Все данные обрабатываются как последовательность байт. Имеется три основных типа функций: потоковые, работающие с консолью и портами ввода-вывода и низкоуровневые.
Потоковые функции.
В потоковых функциях файлы данных рассматриваются как поток отдельных символов.
Когда программа открывает файл для ввода вывода при помощи потоковых функций, то открытый файл связывается с некоторой переменной типа FILE (определенной в stdio.h), содержащей базовую информацию об этом файле. После открытия потока с помощью функции fopen возвращается указатель на эту переменную. Этот указатель используется для ссылки к файлу при всех последующих операциях ввода-вывода и называется указателем потока.
Все потоковые функции обеспечивают буферизованный, форматированный или неформатированный ввод/вывод. Буферизация потока разгружает приложение. Однако следует иметь ввиду, что при аварийном завершении программы содержимое буферов вывода может быть потеряно.
Аналогичным образом выглядят функции, работающие с консолью и портами. Они позволяют читать и писать на терминал или в порт ввода/вывода (например в порт принтера). Функции портов ввода/вывода выполняют простое побайтное считывание или запись. Функции ввода/вывода на консоль обеспечивают несколько дополнительных возможностей, например можно определить момент, когда будет введен символ с консоли и т.п.
Для использования потоковых функций в программу должен быть включен заголовочный файл stdio.h. В нем содержатся описания прототипов функций ввода/вывода, а также — описания ряда констант. Константа EOF определяется как значение, возвращаемое при обнаружении конца файла, BUFSIZE — размер буферов потока, тип FILE определяет структуру, используемую для получения информации о потоке.
Поток открывается с помощью функций fopen(), fdopen() или freopen(). В момент открытия задаются режим файла и способ доступа. Эти функции возвращают указатель на переменную типа FILE, например
FILE *file1;
file1=fopen(“input.dat”,”r”);
открывает файл input.dat для чтения. Переменная file1 получает указатель на поток.
Возможные типы доступа:
“a” – запись в режиме добавления в конец файла,
”a+” –тоже, что и “a”, но возможно и чтение,
”r” – только для чтения,
”r+” – для чтения и записи,
”w” – открывается пустой файл для записи,
”w+” – открывается пустой файл для записи и чтения.
Когда начинается выполнение приложения автоматически открывается следующие потоки: стандартное устройство ввода stdin, стандартное устройство вывода stdout, устройство сообщений об ошибках stderr, устройство печати stdprn и стандартное дополнительное устройство stdaux. Эти файловые указатели можно использовать во всех функциях ввода/вывода в качестве указателя на поток. Некоторые функции автоматически используют указатели на поток, например getchar() и putchar() используют stdin и stdout соответственно.
Для закрытия потоков используются функции fclose() и fcloseall(). Если программа не закрывает поток явно, то он закрывается автоматически по ее завершению.
Для перемещения по файлу можно использовать функции fseek(), ftell() и rewind().
Низкоуровневый ввод и вывод.
Функции низкоуровневого ввода-вывода не выполняю никакой буферизации и форматирования. Они непосредственно обращаются к средствам ввода/вывода операционной системы.
При открытии файла на этом уровне возвращается описатель файла (file handle), представляющий собой целое число, используемое затем для обращения к этому файлу при дальнейших операциях. Для открытия используется функция open(), для закрытия – функция close().
Функция read() читает данные в указанный массив, а write() – выводит данные из массива в файл, lseek() – используется для перемещения по файлу.
Низкоуровневые функции не требуют включения заголовочного файла stdio.h, вместо него используется файл io.h.
Низкоуровневая система ввода-вывода не вошла в стандарт ANSI C, поэтому ее не рекомендуют для дальнейшего использования.
Ввод и вывод символов, строк, слов.
Наиболее общими функциями являются те, которые работают с отдельными символами. Функция getc() вводит один символ из указанного файлового потока в переменную типа int:

int ic;
ic=getc(stdin);

Вводится один символ из потока stdin.
Функция putc() передает один символ в указанный файловый поток:

Для стандартных потоков stdin и stdout можно использовать функции getchar() и putchar() соответственно:

int ic;
ic=getchar();
putchar(ic);

Функции getch() и putch() являются низкоуровневыми функциями. Обычно функция getch() используется для перехвата символа, введенного с клавиатуры сразу после его нажатия. Нажатия клавиши “Enter” не требуется.
Для ввода/вывода текстовых строк можно использовать функции gets(), fgets(), puts(), fputs(). Функция fgets() имеет вид:
fgets(имя_массива, размер_массива, указатель_на_поток);
Она считывает символы в указанный массив и добавляет в конце null-символ. Считывание производится до заполнения массива, или до достижения конца файла. Символ перевода строки переписывается в массив.
Для ввода и вывода целых чисел можно использовать функции getw() и putw(). Они работают с двоичными файлами.
Форматированный ввод и вывод.
Богатый ассортимент средств управления форматом позволяет легко создавать таблицы, графики или отчеты. Функциями, выполняющими этот вывод являются printf() — для стандартного потока вывода stdout, и fprintf() – для любого потока. Функция fprintf() имеет вид:
fprintf(поток_вывода, “формат”, перем_1, перем_2,…);
Работает она аналогично printf() и выводит данные в указанный поток вывода.
Для форматированного ввода используются функции scanf() и fscanf().
Для преобразования текстовой строки можно использовать sscanf(). Она работает аналогично fscanf(), но данные берет из сроки, а не из файла.
Потоки cin, cout, cerr.
В языке С++ имеется другая библиотека ввода/вывода, определяемая заголовочным файлом iostream.h. Ввод/вывод в ней определяется набором специальных классов. Аналогами потоков stdin, stdout и stderr являются cin, cout и cerr. Они открываются автоматически при запуске программы.
Операции выделения >> и вставки > выделяет данные из входного потока и помещает в указанные переменные, а операция >ivalue>>dvalue>>cvalue;

Аналогично для вывода:

printf(“Integer:%d double: %lf”,ivalue,dvalue);
cout
#include
using namespace std;

int main()
<
int num[10];

for(int i=0; i
#include
#include
using namespace std;


int main()
<
int num[10];
int i, j, k;
int size = 10;

// Меняем элементы местами.
i = num[k-1];
num[k-1] = num[k];
num[k] = i;
>
>
>
// Конец сортировки.

// Отображаем отсортированный массив.
cout
#include
using namespace std;

Работа с бинарными файлами в стиле STL

Постановка задачи

Работа с бинарными файлами — традиционная тема при обучении программированию — по меньшей мере, в России. Не последнюю роль здесь играет широкое распространение в российских школах языка программирования Pascal, который имеет встроенную поддержку работы с так называемыми типизированными файлами (типы file of integer , file of real и т. п.). В некоторых случаях (при углублённом изучении программирования у школьников или на младших курсах университета), когда начинается изучение языка C++, возникает желание решать задачи на обработку «файлов типа T» уже в новом окружении данного языка. И тут возникает вопрос, какие средства при этом использовать.

К сожалению, для работы с бинарными файлами в языке C++ предусмотрены только низкоуровневые средства — методы read / write стандартных типов потоков istream / ostream . Кроме других очевидных недостатков этот факт не даёт использовать в полную силу программирование «в стиле STL» (то есть, в первую очередь, часть стандартной библиотеки C++, связанную с алгоритмами и итераторами).

Итак, задача состоит в том, чтобы обеспечить работу с бинарными файлами, хранящими последовательность значений типа T , как с последовательностью STL ( vector и т. п.). Для T подразумеваются значения базовых типов, а также, так называемые POD-типы (везде дальше можно думать только о базовых типах, если вы не знакомы с понятием POD).

Возможные решения

ios_base::binary: fail #0

Если вы никогда не встречались с подобной задачей (что было бы странно!), возможно, вы вспомните что-то про флаг ios_base::binary , но те, кто встречался, хорошо знает, что данное средство практически никак не поможет в решении. Стандарт языка предельно краток на тему того, какого эффекта можно ожидать от указания данного флага при открытии потока, но в сети можно встретить пояснение, что он просто отключает платформенно-зависимые трансляции символов перехода на новые строки и, возможно, ещё некоторых символов, что не имеет прямого отношения к нашей задаче.

Статический полиморфизм: fail #1

Вероятно, люди, знающие стандартную библиотеку чуть глубже, чем на базовом уровне, помнят, что стандартные типы для файловых потоков ofstream / ifstream являются синонимами для явных инстанций шаблонов basic_ofstream / basic_ifstream . Эти шаблоны имеют два аналогичных типовых параметра: тип символов потока (назовём этот параметр Ch ) и тип характеристик типа символов — по умолчанию это std::char_traits . Для ofstream / ifstream в качестве Ch взят тип char .

Здесь немедленно возникает мысль попробовать инстанцировать эти шаблоны с тем типом T , который мы хотим читать из бинарного файла, указав его в качестве значения шаблонного параметра Ch . Простейший код, пытающийся читать из потока такого типа значения типа int падает с исключением времени выполнения bad_cast . Возможно, что-то можно было бы изменить, написав свою специализацию для char_traits и передав её вторым параметром шаблону класса файлового потока, однако этот путь показался беспеперспективным (писать специализацию весьма обширного шаблона char_traits для каждого типа T …) и я не стал разбираться с ним далее.

ООП и динамический полиморфизм: fail #2

После первых неудач можно прийти к мысли, что получить нужное поведение «совсем бесплатно» из стандартных средств не получится и придётся написать кое-какой код. Попробуем решить эту задачу в парадигме ООП, то есть написав пару-другую своих классов. Классов потоков, естественно. Отнаследоваться у стандартных ofstream / ifstream , сохранив максимум определений из предков, и посмотреть, что получится. (В скобках замечу, что задача эта сама по себе не лишена смысла хотя бы ввиду того, что отмечена довольно высоким рейтингом сложности в списке упражнений из книги Б. Страуструпа — упр. 15, п. 21.10 в третьем и специальном изданиях книги «Язык программирования C++».)

С самого начала очевидной была необходимость перегрузки операций и >> для своих классов потоков. Казалось, этого будет достаточно. Проблема возникла в следующем. Чтобы работать с потоком с помощью алгоритмов стандартной библиотеки, следует использовать итераторы ввода/вывода. По заявлениям авторов STL её средства все из себя обобщённые и я ожидал, что как только мой класс потока будет удовлетворять некоторым неявным требованиям библиотеки, она с радостью заработает с ним — статический полиморфизм… В частности, я ожидал, что стандартные итераторы параметризованы типом потока, с которым они работают. Не тут-то было! В определении шаблонов итераторов ввода/вывода жёстко зашиты стандартные типы basic_ofstream / basic_ifstream .

Надежда на спасение на этом пути остаётся, если обратить внимание на одну особенность реализации операций и >> : для базовых типов они реализованы как функции-члены шаблонов классов потоков. Если бы они, кроме того, были объявлены виртуальными, то можно было бы положиться на динамический полиморфизм (стандартные итераторы хранили бы объекты моих потоков по ссылке на базовый класс) — получилось бы частичное решение исходной задачи, работавшее только для базовых типов (int, double и т. п.). Однако указанные функции-члены не являются виртуальными. Тут можно было бы порассуждать о логике устройства стандартной библиотеки или отсутствии таковой (например, известно, что для STL изначально не предполагалось использования всей мощи ООП, наследования и полиморфизма, но ведь потоковая библиотека построена на ООП…), однако перейдём к финальному решению.

Ad-hoc полиморфизм (перегрузка): win

В конце концов, всё, что требуется — вызывать специальные версии операций и >> , которые бы прятали низкоуровневую работу с файлами посредством read / write . Достаточно предоставить свою перегрузку этих операций и позаботиться о том, чтобы вызывалась именно она. Достичь этого можно использованием специальных типов в аргументах. Манипулировать типами потоков у нас уже не получилось — остаётся придумать специальные типы для вводимого/выводимого. Здесь напрашивается использование того, что называется «обёртками».

К счастью, нам нет нужны писать новые классы обёрток для разных типов T : можно ограничиться одним шаблоном класса, хранящим поле параметра-типа T и умеющего преобразовываться к ссылке на это поле — константной и неконстантной. Конструктор с одним параметром типа T , который не объявлен как explicit , позволит неявно преобразовывать значения типа T к типу-обёртке. Конструктор без параметров — требование STL. Результирующий код приведён ниже.

Цукерберг рекомендует:  Новичок - Помогите с задачей, я новичок в С++

Использование static_cast требует от компилятора вызвать определённую в теле шаблона класса операцию приведения типа для получения ссылки на информационное поле, а reinterpret_cast приводит адрес этого поля к указателю на char , готовя нас к низкоуровневой работе с read / write .

Вот пример, демонстрирующий использование обёртки. Он несёт на себе отпечаток тех идей, которые закладывались изначально, а именно, программирование в стиле STL.

Заключение

Понятно, что в результате получился «абсолютный велосипед», который, наверное, писался многими программистами на C++, однако в сети или в каких-то известных библиотеках (например, Boost, в частности, Boost.Iostreams) подобного я не заметил.

Ещё я хотел бы отметить, что намерено оставил за скобками обсуждение актуальности поставленной задачи. Наверное, есть люди, не представляющие работу с файлами на более высоком, чем read / write , уровне. Возможно, найдутся те, кто скажет, что бинарные файлы это прошлое, что они жутко непереносимы или что-то похожее. Может быть, отчасти это так, однако само упражнение в решении такой задачи мне показалось интересным и познавательным.

За постановку задачи и обсуждение решения я искренне благодарю Виталия Николаевича Брагилевского.

UPD1: в комментариях спросили, почему вместо преобразований типов не написать обычные функции-члены get. Преобразования по существу используются в примерах наподобие следующего.

1. «Классический» ввод/вывод

В языке С определены три стандартных потока ввода/вывода:

  1. stdin – стандартное устройство ввода (клавиатура);
  2. stdout – стандартное устройство вывода (экран);
  3. stderr – стандартное устройство вывода сообщения об ошибках (также экран).

Простейший ввод/вывод:

  • getch() – ввод одного символа без отображения его на экране;
  • getche() – ввод одного символа с отображением его на экране;
  • putchar(int c) – вывод одного символа на экран.

Все эти функции требуют включения заголовочного файла .

Все остальные функции ввода/вывода определены в заголовочном файле .

1.1. Ввод/вывод с экрана

Для ввода и вывода на экран используются функции scanf и printf соответственно, прототипы которых имеют следующий вид: int scanf ( char * format , . ); int printf ( char * format , . );

Вывод осуществляется функцией printf, которая имеет следующий синтаксис: printf( [, ]);

Строка описания форматов состоит из обычных символов, специальных управляющих последовательностей символов и спецификаций формата.

Обычные символы и управляющие последовательности просто копируются в стандартный выходной поток в порядке их появления.

Спецификации формата начинаются с символа % и заканчиваются символом, определяющим тип выводимого значения. Кроме того, спецификации формата могут содержать символы и цифры для управления видом выводимого значения (подробно см. ниже). Список вывода состоит из переменных и/или констант, значения которых должны быть выведены. Количество спецификаций формата должно быть равно количеству выводимых значений, которые указываются в списке вывода. Если это условие не будет соблюдаться, выполнение функции может привести к непредсказуемым результатам.

Ввод осуществляется функцией scanf, которая имеет следующий синтаксис: scanf( [, ]);

Строка описания форматов состоит из набора спецификаций формата, таких же, как для функции printf. Список ввода состоит из адресов переменных, куда будут заноситься вводимые значения. Адрес переменной вычисляется с помощью унарной операции &. Количество спецификаций формата должно быть равно количеству вводимых значений, которые указываются в списке ввода.

Функция scanf возвращает количество успешно введенных и преобразованных значений. Функция printf возвращает количество символов, записанных в выходной поток.

К управляющим последовательностям относятся следующие последовательности символов.

Последовательность Дейcтвие
\a Звуковой сигнал
\b Удаление предыдущего символа
\n Новая строка
\r Возврат каретки
\t Табуляция
\’ Апостроф
Кавычки
\\ Обратный слеш
\ooo ASCII символ в восьмеричной нотации
\xooo ASCII символ в шестнадцатеричной нотации

Спецификация формата, которая состоит из обязательных и необязательных полей, имеет следующий вид:

Флаги Значение По умолчанию
Выравнивание по левому краю. Выравнивание по правому краю.
+ Добавление знака + или – перед числами. Знак добавляется только перед отрицательными числами.
Добавление нулей перед выводимым значением. Если одновременно используются флаги – и 0, 0 игнорируется. Добавление пробелов.
пробел Добавление пробела перед положительным числом. Если одновременно используются флаги пробел и +, пробел игнорируется. Пробел не добавляется.
# Добавление символов 0, 0х или 0Х перед ненулевым значением, если флаг # используется с форматами о, х или Х соответственно. Символы 0, 0х и 0Х не добавляются.
При использовании с форматами e, E и f флага # выводимое число будет содержать десятичную точку в любом случае. Десятичная точка добавляется, только если за ней следуют цифры.
При использовании с форматами g и G флага # выводимое число будет содержать десятичную точку и хвостовые нули в любом случае. Десятичная точка добавляется, только если за ней следуют цифры. Хвостовые нули не выводятся.
Игнорируется при использовании с форматами c, d, i, u и s.

Поле ширина содержит минимальное количество выводимых символов – неотрицательное целое число. Если выводимое значение содержит меньше символов, то оно расширяется пробелами (если не задан флаг ). Если поле ширина содержит звездочку (*), то в качестве значения поля берётся целое число из списка аргументов, предшествующее выводимому значению.

Поле точность также представляет собой неотрицательное целое число. Действие зависит от типа выводимого значения.

Тип Действие По умолчанию
c, C Точность не имеет эффекта. Выводится символ.
d, i, u, o, x, X Точность задаёт минимальное количество символов, которые будут напечатаны. Если число содержит меньше символов, оно расширяется нулями. Точность равна 1.
e, E, f Точность задаёт количество символов после десятичной точки. Число округляется. Точность равна 6. Если точность равна 0 или опущена, десятичная точка не выводится.
g, G Точность задаёт максимальное количество значащих цифр. Печатается 6 значащих цифр.
s, S Точность задаёт максимальное количество выводимых символов. Выводятся все символы строки.

Если поле точность содержит звездочку (*), то в качестве значения поля берётся целое число из списка аргументов, предшествующее выводимому значению.

Дополнительные префиксы h, l, L и I64 задают «размер» аргумента – long или short, однобайтовый символ или расширенный символ, в зависимости от спецификации типа, которую они модифицируют.

Обязательное поле тип задаёт тип выводимого значения.

Символ Тип Формат вывода
c int или wint_t При использовании с функцией printf определяет однобайтовый символ, при использовании с функцией wprintf определяет расширенный символ.
C int или wint_t При использовании с функцией printf определяет расширенный символ, при использовании с функцией wprintf определяет однобайтовый символ.
d int Знаковое десятичное целое.
i int Знаковое десятичное целое.
o int Беззнаковое восьмеричное целое.
u int Беззнаковое десятичное целое.
x int Беззнаковое шестнадцатеричное целое с использованием символов «abcdef».
X int Беззнаковое шестнадцатеричное целое с использованием символов «ABCDEF».
e double Знаковое число в форме [ – ]d.dddd e [знак]ddd, где d есть одна десятичная цифра, dddd – одна или более десятичных цифр, ddd – три десятичные цифры and знак есть + или –.
E double Идентичен формату e, за исключением того, что символ E, а не e вводит экспоненту.
f double Знаковое число в форме [ – ]dddd.dddd, где dddd есть одна или более десятичных цифр. Количество цифр перед десятичной точкой зависит от величины числа, а количество цифр после десятичной точки – от требуемой точности.
g double Знаковое число в формате f или e, в зависимости от того, какой формат более компактен для заданного значения и точности.
G double Идентичен формату g, за исключением того, что символ E, а не e вводит экспоненту.
n pointer to integer Количество символов успешно записанных к данному моменту в выходной поток. Это значение сохраняется в целочисленной переменной, чей адрес задан как аргумент.
p pointer to void Печатает адрес, заданный аргументом.
s string При использовании с функцией printf задаёт строку однобайтовых символов, при использовании с функцией wprintf задаёт строку расширенных символов. Символы печатаются до достижения признака конца строки.
S string При использовании с функцией printf задаёт строку расширенных символов, при использовании с функцией wprintf задаёт строку однобайтовых символов. Символы печатаются до достижения признака конца строки.

int m, n, x; double y; char c = ‘&’; char str[] = «String»;
scanf(«%d%d», &m, &n); // Ввод десятичных целых чисел в переменные m и n
printf(«m = %5d\nn = %5d\n», m, n); // Вывод переменных m и n в десятичном целом формате, используются как минимум 5 знаков
scanf(«%d», &x); // Ввод десятичного целого числа в переменную x
printf(«%#010x\n», x); // Вывод переменной x в шестнадцатеричной системе, используются 10 знаков, // впереди добавляются нули и символы
scanf(«%lf», &y); // Ввод вещественного числа в переменную y
printf(«y = %7.2lf\n», y); // Вывод вещественной переменной, используются как минимум 7 знаков, из них 2 – после точки
printf(«c = %c\n», c); // Вывод одного символа
printf(«%.4s\n», str); // Вывод строки (не более 4 символов)

1.2. Чтение из строки/запись в строку

Функции sprintf и sscanf позволяют произвести запись значений переменных в форматированную строку или чтение переменных из строки: int sscanf ( char * str , char * format , . ); int sprintf ( char * str , char * format , . );

Эти функции во всём аналогичны функциям printf и scanf, только в качестве первого параметра указывается строка, куда записываются или откуда считываются данные.

1.3. Работа с файлами

  • Объявление файловой переменной
– FILE * file ;
  • Открытие файла
– FILE * fopen ( char * name , char * mode );
  • Проверка достижения конца файла
– int feof ( FILE * file );
  • Закрытие файла
– int fclose ( FILE * file );

FILE – специальная структура, объявленная в файле , которая используется при работе с файлами. Для работы с файлом нужно объявить переменную FILE * .

Функция fopen используется для открытия файла. Первый параметр задаёт имя файла. Второй параметр mode задаёт требуемый тип доступа к файлу.

Mode Действие
«r» Открытие для чтения. Если файл не существует или не может быть найден, функция fopen возвращает признак ошибки.
«w» Открытие для записи. Если файл существует, его содержимое уничтожается. Если файл не существует, он создаётся.
«a» Открытие для добавления. Если файл не существует, он создаётся.
«r+» Открытие для чтения и записи. Файл должен существовать.
«w+» Открытие пустого файла для чтения и записи. Если файл существует, его содержимое уничтожается.
«a+» Открытие для чтения и добавления. Если файл не существует, он создаётся.

Кроме того, к параметру mode могут быть добавлены символы t и b для задания текстового и двоичного режимов соответственно. По умолчанию используется текстовый режим.

В случае ошибки функция fopen возвращает значение NULL.

1.3.1. Текстовый режим

При вводе/выводе в текстовом режиме происходит преобразование между внешним представлением значения и внутренним (машинным) представлением этого значения.

  • Ввод одного символа
– int getc ( FILE * file );
  • Вывод одного символа
– int putc ( int c , FILE * file );
  • Ввод
– int fscanf ( FILE * file , char * format , . );
  • Вывод
– int fprintf ( FILE * file , char * format , . );
  • Ввод строки
– char * fgets ( char * line , int maxline , FILE * file );
  • Вывод строки
– int fputs ( char * line , FILE * file );

1.3.2. Двоичный режим

В двоичном режиме никаких преобразований не производится, внутреннее представление значения записывается в файл. Для открытия файла в двоичном режиме необходимо в параметр mode функции fopen добавить символ b.

  • Ввод из двоичного файла
– unsigned fread ( void * buf , int bytes , int num , FILE * file );
  • Вывод в двоичный файл
– unsigned fwrite ( void * buf , int bytes , int num , FILE * file );

Функция fread читает из файла file в переменную buf num элементов, каждый размером bytes байт. Функция fwrite записывает в файл file из переменной buf num элементов, каждый размером bytes байт. Функции возвращают количество прочитанных/записанных элементов.

В двоичном режиме возможен прямой доступ к файлу: int fseek(FILE *file, long nbytes, int origin)

Данная функция смещает указатель в файле file на nbytes байт с позиции, определяемой параметром origin. При этом параметр origin может принимать следующие значения:

  • SEEK_SET 0 – начало файла;
  • SEEK_CUR 1 – текущая позиция указателя;
  • SEEK_END 2 – конец файла.

Функция long ftell(FILE *file) возвращает текущую позицию указателя в файле file.

fseek(file, 0, SEEK_END); n = ftell(file); // Определение размера файла

2. Потоковый ввод/вывод

В языке С++ был разработан другой способ ввода/вывода с использованием так называемых потоков ввода и вывода.

2.1. Использование стандартных потоков cin и cout

Для того чтобы использовать стандартные потоки для ввода и вывода, необходимо включить заголовочный файл . Для ввода используется операция >>, для вывода – операция #include using namespace std; cin >> x; // Ввод значения в переменную x из стандартного потока cin cout > x >> y; // Ввод двух переменных cout > x; if (cin.fail()) cout .

Несколько манипуляторов имеют параметр, который может быть задан литералом или переменной. Изменения, сделанные всеми манипуляторами, кроме setw, остаются в силе до отмены. Действие манипулятор setw распространяется только на одно вводимое/выводимое значение.

Манипулятор Описание Примечание
boolalpha Значения переменных типа bool выводятся как true и false.
dec Целые значения выводятся в десятичной системе счисления. Используется по умолчанию
fixed Для вещественных чисел используется фиксированный формат.
hex Целые значения выводятся в шестнадцатеричной системе счисления.
internal Знак выравнивается по левому краю, а само число – по правому краю.
left Выравнивание по левому краю.
noboolalpha Значения переменных типа bool выводятся как 1 и . Используется по умолчанию
noshowbase Префиксы и , обозначающие систему счисления, не выводятся. Используется по умолчанию
noshowpoint Вывод только целой части вещественного числа (без точки), если дробная часть равна 0. Используется по умолчанию
noshowpos Знак перед положительными числами не выводится. Используется по умолчанию
noskipws Пробел рассматривается как признак завершения ввода.
nouppercase Шестнадцатеричные цифры и символ экспоненты в научном формате вещественного числа выводятся строчными буквами. Используется по умолчанию
oct Целые значения выводятся в восьмеричной системе счисления.
right Выравнивание по правому краю. Используется по умолчанию
scientific Для вещественных чисел используется научный формат.
setfill(c) Задаёт символ для заполнения. По умолчанию используется пробел.
setprecision(n) Задаёт точность для вещественных чисел. По умолчанию точность равна 6. Если не установлен ни фиксированный, ни научный формат вещественного числа, то точность задаёт количество выводимых цифр (всего, до точки и после точки). Если число слишком велико, оно автоматически отображается в научном формате, и тогда точность задаёт количество цифр в мантиссе. Если установлен фиксированный формат вещественного числа, точность задаёт количество цифр после точки. Если установлен научный формат вещественного числа, точность задаёт количество цифр в мантиссе.
setw(n) Устанавливает минимальное количество символов, используемых для вывода значения. Если значение представляется меньшим количеством символов, остальные позиции заполняются символом, установленным с помощью манипулятора setfill. Выравнивание задаётся манипуляторами left, right и internal. Чтобы установить поведение по умолчанию (столько символов, сколько необходимо), нужно использовать манипулятор setw с параметром . Влияет только на одно вводимое/выводимое значение!
showbase Вывод префиксов и для обозначения системы счисления.
showpoint Вывод и целой, и дробной частей вещественного числа, даже если дробная часть равна 0.
showpos Вывод знака перед положительным числом.
skipws Пробелы рассматриваются как разделители между значениями. Используется по умолчанию
uppercase Шестнадцатеричные цифры и символ экспоненты в научном формате вещественного числа выводятся прописными буквами.
int m, n, x; double y; cin >> m >> n; cout > x; cout > y; cout char c; cout > c;

Как нам гарантировать, что слова Введите символ появятся на экране прежде, чем будет выполнена операция считывания? Вывод в стандартный поток буферизуется, так что если потоки cin и cout не зависимы, то выводимый текст не появится на экране, пока не заполнится буфер вывода. Решение этой задачи заключается в том, что потоки связываются с помощью функции tie. Эта функция используется для того, чтобы устанавливать и разрывать связи между потоками ввода и вывода.
char c; cin.tie(&cout); cout > c;

В каждый момент времени поток ввода может быть связан только с одним потоком вывода. Обращение s.tie(0) разрывает связь между потоком s и потоком, с которым он был связан. Как и большинство функций с потоками, устанавливающих значение, функция tie возвращает прежнее значение, т.е. она возвращает предыдущий связанный поток или 0. При вызове без аргументов функция tie() возвращает текущий связанный поток, не изменяя его.

2.4. Файловые потоки

Для ввода/вывода из файла/в файл существуют потоки, которые могут быть связаны с файлом на диске. Для использования файловых потоков необходимо включить заголовочный файл . Существует три разновидности файловых потоков: fstream, ifstream и ofstream. Разница между ними состоит в том, что поток fstream по умолчанию открывается для ввода и вывода, поток ifstream по умолчанию открывается для ввода, а поток ofstream по умолчанию открывается для вывода. Изменить поведение по умолчанию, а также задать другие режимы открытия файла можно с помощью следующих констант:

  • ios_base::app – открытие файла для добавления;
  • ios_base::binary – открытие двоичного, а не текстового файла;
  • ios_base::in – открытие файла для чтения;
  • ios_base::out – открытие файла для записи;
  • ios_base::trunc – удаление содержимого файла при открытии.

Режимы открытия файла комбинируются с помощью операции поразрядного ИЛИ (|).

Для открытия файла можно задать имя файла непосредственно в конструкторе потока или воспользоваться функцией open.

fstream fs(«f1.txt»); // Открытие файла для чтения и записи
ifstream ifs(«f2.txt»); // Открытие файла для чтения
ofstream ofs(«f3.txt»); // Открытие файла для записи
fstream fs(«f1.txt», ios_base::in | ios_base::out | ios_base::trunk); // Открытие файла для чтения и записи с удалением содержимого файла
ifstream ifs(«f2.txt», ios_base::in | ios_base::binary); // Открытие двоичного файла для чтения
ofstream ofs; // Создаём поток, не связанный с файлом
ofs.open(«f3.txt»); // Открываем файл для записи

Если вы планируете использовать файл только для чтения или только для записи безопаснее воспользоваться соответствующим файловым потоком.

Для проверки открытия файла служит функция is_open.

Потоки автоматически закрываются при завершении программы. Однако при необходимости можно закрыть поток функцией close и затем снова открыть его, связав с другим файлом.

Для работы с текстовыми потоками используются операции >. Также возможно использование манипуляторов для форматирования вводимых/выводимых значений.

int x; fstream f;
f.open(«in.txt», ios_base::in); // Открываем файл для чтения
if (!f.is_open()) // Проверяем открытие файла
f >> x; // Чтение переменной x из файла
if (f.fail()) // Проверка ошибок чтения
f.close(); // Закрываем файл
f.open(«out.txt», ios_base::out); // Снова открываем файл, теперь для записи
if (!f.is_open()) // Проверяем открытие файла
f int n; ifstream f(«in.txt»);
if (!f.is_open())
while (!f.eof()) // Пока не достигнут конец файла
< f >> n; cout ios_base::beg – начало файла;
  • ios_base::cur – текущая позиция указателя;
  • ios_base::end – конец файла.
  • Функции tellg/tellp не имеют параметров. Они возвращают текущую позицию указателя в файле.

    Функции seekg/seekp и tellg/tellp работают как с текстовыми, так и с двоичными потоками. В любом случае желательно либо знать структуру файла, либо работать с файлами, все записи в которых имеют одинаковую длину. В противном случае возможно перемещение указателя на позицию, не являющуюся началом записи.

    Для работы с двоичными файлами используются функции read и write. В качестве параметров функции получают указатель (типа char* для функции read и типа const char* для функции write), который задаёт адрес начала массива для ввода/вывода, и целое число, задающее количество байт для ввода/вывода.

    Пример использования потокового ввода из двоичного файла см. в конце лекции.

    Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих