Потоковый ввод-вывод в стиле С
Особенностью С является отсутствие в этом языке структурированных файлов. Все файлы рассматриваются как не структурированная последовательность байтов. При таком подходе понятие файла распространяется и на различные устройства.
В С существуют средства ввода-вывода. Все операции ввода-вывода реализуются с помощью функций, которые находятся в библиотеке С. Библиотека С поддерживает три уровня ввода-вывода:
· потоковый ввод-вывод;
· ввод-вывод нижнего уровня;
· ввод-вывод для консоли и портов (зависит от ОС).
Поток– это абстрактное понятие, относящееся к любому переносу данных от источника к приемнику.
Чтение данных из потока называется извлечением, вывод в поток – помещением, или включением.
Поток определяется как последовательность байтов и не зависит от конкретного устройства, с которым производится обмен (оперативная память, файл на диске, клавиатура или принтер). Обмен с потоком для увеличения скорости передачи данных производится, как правило, через специальную область оперативной памяти — буфер. Буфер накапливает байты, и фактическая передача данных выполняется после заполнения буфера. При вводе это дает возможность исправить ошибки, если данные из буфера еще не отправлены в программу.
При работе с потоком можно:
· Открывать и закрывать потоки (связывать указатели на поток с конкретными файлами);
· вводить и выводить строку, символ, форматированные данные, порцию данных произвольной длины;
· анализировать ошибки ввода-вывода и достижения конца файла;
· управлять буферизацией потока и размером буфера;
· получать и устанавливать указатель текущей позиции в файле.
Функции библиотеки ввода-вывода находятся в заголовочном файле <stdio.h>.
Прежде чем начать работать с потоком, его надо инициировать, т. е. открыть. При этом поток связывается со структурой предопределенного типа FILE, определение которой находится в библиотечном файле <stdio.h>. В структуре находится указатель на буфер, указатель на текущую позицию файла и т. п. При открытии потока, возвращается указатель на поток, т. е. на объект типа FILE.
#include <stdio.h>;
. . . . . . . .
FILE *fp;
. . . . . . . . . . ..
fp= fopen( ”t.txt”, ”r”);
где fopen(<имя_файла>,<режим_открытия>) - функция для инициализации файла.
Существуют следующие режимы для открытия файла:
Режим | Описание режима открытия файла |
r | Файл открывается для чтения, если файл не существует , то выдается ошибка при исполнении программы. |
w | Файл открывается для записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается. |
a | Файл открывается для добавления, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла |
r+ | Файл открывается для чтения и записи, изменить размер файла нельзя, если файл не существует , то выдается ошибка при исполнении программы. |
w+ | Файл открывается для чтения и записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается. |
a+ | Файл открывается для чтения и записи, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла |
Поток можно открыть в текстовом (t) или двоичном (b) режиме. По умолчанию используется текстовый режим. В явном виде режим указывается следующим образом:
· ”r+b”или ”rb” - двоичный (бинарный) режим;
· ”r+t” или ”rt” – текстовый режим.
В файле stdio.h определена константа EOF, которая сообщает об окончании файла (отрицательное целое число).
При открытии потока могут возникать следующие ошибки:
· файл, связанный с потоком не найден (при чтении из файла);
· диск заполнен (при записи);
· диск защищен от записи (при записи) и т. п.
В этих случаях указатель на поток приобретет значение NULL (0). Указатель на поток, отличный от аварийного не равен 0.
Для вывода об ошибке при открытии потока используется стандартная библиотечная функция из файла <stdio.h>
void perror (const char*s);
if ((fp=fopen(”t.txt”, ”w”)==NULL)
{
// выводит строку символов с сообщением об ошибке
perror(”\nошибка при открытии файла”);
exit(0);
}
После работы с файлом, его надо закрыть
fclose(<указатель_на_поток>);
Когда программа начинает выполняться, автоматически открываются несколько потоков, из которых основными являются:
· стандартный поток ввода (stdin);
· стандартный поток вывода (stdout);
· стандартный поток вывода об ошибках (stderr).
По умолчанию stdin ставится в соответствие клавиатура, а потокам stdout и stderr - монитор. Для ввода-вывода с помощью стандартных потоков используются функции:
· getchar()/putchar() – ввод-вывод отдельного символа;
· gets()/puts() – ввод-вывод строки;
· scanf()/printf() – форматированный ввод/вывод.
Аналогично работе со стандартными потоками выполняется ввод-вывод в потоки, связанные с файлами.
Для символьного ввода-вывода используются функции:
· int fgetc(FILE*fp), где fp – указатель на поток, из которого выполняется считывание. Функция возвращает очередной символ в форме int из потока fp. Если символ не может быть прочитан, то возвращается значение EOF.
· int fputc(int c, FILE*fp), где fp – указатель на поток, в который выполняется запись, c – переменная типа int, в которой содержится записываемый в поток символ. Функция возвращает записанный в поток fp символ в форме int . Если символ не может быть записан, то возвращается значение EOF.
Для построчного ввода-вывода используются следующие функции:
· char* fgets(char* s,int n,FILE* f), где char*s – адрес, по которому размещаются считанные байты, int n – количество считанных байтов, FILE* f – указатель на файл, из которого производится считывание.
Прием байтов заканчивается после передачи n-1 байтов или при получении управляющего символа ‘\n’. Управляющий символ тоже передается в принимающую строку. Строка в любом случае заканчивается ‘\0’. При успешном завершении работы функция возвращает указатель на прочитанную строку, при неуспешном – 0.
· int puts(char* s, FILE* f), где char*s – адрес, из которого берутся записываемые в файл байты, FILE* f – указатель на файл, в который производится запись.
Символ конца строки (‘\0’) в файл не записывается. Функция возвращает EOF, если при записи в файл произошла ошибка, при успешной записи возвращает неотрицательное число.
Для блокового ввода-вывода используются функции:
· int fread(void*ptr,int size, int n, FILE*f), где void*ptr – указатель на область памяти, в которой размещаются считанные из файла данные, int size – размер одного считываемого элемента, int n – количество считываемых элементов, FILE*f – указатель на файл, из которого производится считывание.
В случае успешного считывания функция возвращает количество считанных элементов, иначе – EOF.
· int fwrite(void*ptr,int size, int n, FILE*f), где void*ptr – указатель на область памяти, в которой размещаются считанные из файла данные, int size – размер одного записываемого элемента, int n – количество записываемых элементов, FILE*f – указатель на файл, в который производится запись.
В случае успешной записи функция возвращает количество записанных элементов, иначе – EOF.
В некоторых случаях информацию удобно записывать в файл без преобразования, т. е. в символьном виде пригодном для непосредственного отображения на экран. Для этого можно использовать функции форматированного ввода-вывода:
· int fprintf(FILE *f, const char*fmt,. . .) , где FILE*f – указатель на файл, в который производится запись, const char*fmt – форматная строка, . . . – список переменных, которые записываются в файл.
Функция возвращает число записанных символов.
· int fscanf(FILE *f, const char*fmt, par1,par2, . . .) , где FILE*f – указатель на файл, из которого производится чтение, const char*fmt – форматная строка, par1,par2,. . . – список переменных, в которые заносится информация из файла.
Функция возвращает число переменных, которым присвоено значение.
Средства прямого доступа дают возможность перемещать указатель текущей позиции в потоке на нужный байт. Для этого используется функция
· int fseek(FILE *f, long off, int org), где FILE *f - – указатель на файл, long off – позиция смещения, int org – начало отсчета.
Смещение задается выражение или переменной и может быть отрицательным, т. е. возможно перемещение как в прямом, так и в обратном направлениях. Начало отсчета задается одной из определенных в файле <stdio.h> констант:
SEEK_SET ==0 – начало файла;
SEEK_CUR==1 – текущая позиция;
SEEK_END ==2 – конец файла.
Функция возвращает 0, если перемещение в потоке выполнено успешно, иначе возвращает ненулевое значение.
Обработка элементов файла
Для того чтобы удалить элемент из файла нужно использовать вспомогательный файл. Во вспомогательный файл переписываются все элементы исходного файла за исключением тех, которые требуется удалить. После этого исходный файл удаляется из памяти, а вспомогательному файлу присваивается имя исходного файла.
void del(char *filename)
{//удаление записи с номером х
FILE *f;//исходный файл
FILE*temp;//вспомогательный файл
//открыть исходный файл для чтения
f=fopen(filename,”rb”);
//открыть вспомогательный файл для записи
temp=fopen(”temp”,”wb”)
student a;//буфер для чтения данных из файла
//считываем данные из исходного файла в буфер
for(long i=0; fread(&a,sizeof(student),1,f);i++)
if(i!=x)//если номер записи не равен х
{
//записываем данные из буфера во временный файл
fwrite(&a,sizeof(student)1,temp);
}
else
{
cout<<a<<" - is deleting...";
}
fclose(f);//закрываем исходный файл
fclose(temp); //закрываем временный файл
remove(filename);//удаляем исходный файл
rename(”temp”, filename);//переименовываем временный файл
}
Для корректировки элементов файла используется аналогичный алгоритм. Данные из исходного файла переписываются во вспомогательный файл, но записи, которые нужно изменить записываются в откорректированном виде.
Для добавления элементов в начало или в середину файла также используется вспомогательный файл, в который в нужное место добавляются новые данные.
Для добавления элементов конец файла достаточно открыть его в режиме “a” или “a+” (для добавления) и записать новые данные в конец файла.
f=fopen(filename,”ab”);//открыть файл для добавления
cout<<"\nHow many records would you add to file?";
cin>>n;
for(int i=0;i<n;i++)
{
//прочитать объект
fwrite(&a,sizeof(student),1,f);//записать в файл
}
fclose(f);//закрыть файл
2.3. Потоковый ввод-вывод в стиле С++
С++ предоставляет возможность ввода/вывода как на низком уровне – неформатированный ввод-вывод, так и на высоком – форматированный ввод-вывод. При неформатированном вводе/выводе передача информации осуществляется блоками байтов данных без какого-либо преобразования. При форматированном - байты группируются таким образом, чтобы их можно было воспринимать как типизированные данные (целые числа, строки символов, числа с плавающей запятой и т. п.)
По направлению обмена потоки можно разделить на
· входные (данные вводятся в память),
· выходные (данные выводятся из памяти),
· двунаправленные (допускающие как извлечение, так и включение).
По виду устройств, с которыми работает поток, потоки можно разделить на стандартные, файловые и строковые:
· стандартныепотокипредназначены для передачи данных от клавиатуры и на экран дисплея,
· файловыепотоки — для обмена информацией с файлами на внешних носителях данных (например, на магнитном диске),
· строковыепотоки— для работы с массивами символов в оперативной памяти.
Для работы со стандартными потоками библиотека C++ содержит библиотеку <iostream.h>. При этом в программе автоматически становятся доступными объекты:
· cin - объект, соответствует стандартному потоку ввода,
· cout - объект, соответствует стандартному потоку вывода.
Оба эти потока являются буферизированными.
Форматированный ввод/вывод реализуется через две операции:
· << - вывод в поток;
· >> - чтение из потока.
Использование файлов в программе предполагает следующие операции:
· создание потока;
· открытие потока и связывание его с файлом;
· обмен (ввод/вывод);
· уничтожение потока;
· закрытие файла.
Для работы со файловыми потоками библиотека C++ содержит библиотеки:
· <ifstream.h> - для работы с входными потоками,
· <ofstream.h> - для работы с выходными потоками
· <fstream.h> - для работы с двунаправленными потоками.
В библиотечных файлах потоки описаны как классы, т. е. представляют собой пользовательские типы данных (аналогично структурам данных). В описание класса, кроме данных, добавляются описания функций, обрабатывающих эти данные (соответственно компонентные данные и компонентные функции (методы)). Обращаться к компонентным функциям можно также как и к компонентным данным с помощью . или ->.
Для создания файлового потока используются специальные методы –конструкторы, которые создают поток соответствующего класса, открывают файл с указанным именем и связывают файл с потоком:
· ifstream(const char *name, int mode = ios::in);//входной поток
· ofstream(const char *name, int mode = ios::out | ios::trunc);//выходной поток
· fstreamCconst char *name, int mode = ios::in | ios::out);//двунаправленный поток
Вторым параметром является режим открытия файла. Вместо значения по умолчанию можно указать одно из следующих значений, определенных в классе ios.
ios::in | открыть файл для чтения; |
ios::out | открыть файл для записи; |
ios::ate | установить указатель на конец файла, читать нельзя, можно только записывать данные в конец файла; |
ios::app | открыть файл для добавления; |
ios::trunc | если файл существует, то создать новый; |
ios::binary | открыть в двоичном режиме; |
ios::nocreate | если файл не существует, выдать ошибку, новый файл не открывать |
ios::noreplace | если файл существует, выдать ошибку, существующий файл не открывать; |
Открыть файл в программе можно с использованием либо конструкторов, либо метода open, имеющего такие же параметры, как и в соответствующем конструкторе.
fstream f; //создает файловый поток f
//открывается файл, который связывается с потоком
f.open(“..\\f.dat”,ios::in);
// создает и открывает для чтения файловый поток f
fstream f (”..\\f.dat”,ios::in);
После того как файловый поток открыт, работать с ним можно также как и со стандартными потоками cin и cout. При чтении данных из входного файла надо контролировать, был ли достигнут конец файла после очередной операции вывода. Это можно делать с помощью метода eof().
Если в процессе работы возникнет ошибочная ситуация, то потоковый объект принимает значение равное 0.
Когда программа покидает область видимости потокового объекта, то он уничтожается, при этом перестает существовать связь между потоковым объектом и физическим файлом, а сам файл закрывается. Если файл требуется закрыть раньше, то используется метод close().
//создание файла из элементов типа person
struct person
{
char name[20];
int age;
};
person *mas;//динамический массив
fstream f("f.dat",ios::out);//двунаправленный файловый поток
int n;
cout<<"N?";
cin>>n;
mas=new person[n];//создаем динамический массив
for(int i=0;i<n;i++)
{
cout<<"?";
//ввод одного элемента типа person из стандартного потока cin
cin>>mas[i].name;
cin>>mas[i].age;
}
//запись элементов массива в файловый поток
for(i=0;i<n;i++)
{
f<<mas[i].name;f<<"\n";
f<<mas[i].age;f<<"\n";
}
f.close();//закрытие потока
//чтение элементов из файла
person p;
f.open("f.dat",ios::in);//открываем поток для чтения
do
{
/*читаем элементы типа person из файлового потока f в переменную p*/
f>>p.name;
f>>p.age;
// если достигнут конец файла, выходим из цикла
if (f.eof())break;
//вывод на экран
cout<<p.name<<" "<<p.age<<"\n";
}while(!f.eof());
f.close();//закрытие потока
Постановка задачи
- Используя ввод-вывод в стиле С создать файл и записать в него структурированные данные.
- Вывести созданный файл на экран.
- Удалить из файла данные в соответствии с вариантом.
- Добавить в файл данные в соответствии с вариантом.
- Вывести измененный файл на экран.
- Используя ввод-вывод в стиле С++ создать файл и записать в него структурированные данные.
- Вывести созданный файл на экран.
- Удалить из файла данные в соответствии с вариантом.
- Добавить в файл данные в соответствии с вариантом.
- Вывести измененный файл на экран.
№ варианта | Структура данных | Удаление | Добавление |
Структура "Абитуриент": - фамилия, имя, отчество; - год рождения; - оценки вступительных экзаменов (3); - средний балл аттестата. | Удалить элемент с указанным номером. | Добавить K элементов в начало файла | |
Структура "Сотрудник": - фамилия, имя, отчество; - должность - год рождения; - заработная плата. | Удалить элемент с указанной фамилией | Добавить K элементов в конец файла | |
Структура "Государство": - название; - столица; - численность населения; - занимаемая площадь. | Удалить все элементы, у которых численность меньше заданной. | Добавить элемент с номером К | |
Структура "Человек": - фамилия, имя, отчество; - домашний адрес; - номер телефона; - возраст. | Удалить все элементы с заданным возрастом. | Добавить N элементов с номером К | |
Структура "Человек": - фамилия, имя, отчество; - год рождения; - рост; - вес. | Удалить все элементы с указанным ростом и весом. | Добавить K элементов в начало файла | |
Структура "Школьник": - фамилия, имя, отчество; - класс; - номер телефона; - оценки по предметам (математика, физика, русский язык, литература). | Удалить все элементы, у которых есть 2 хотя бы по одному предмету. | Добавить K элементов в конец файла | |
Структура "Студент": - фамилия, имя, отчество; - домашний адрес; - группа; - рейтинг. | Удалить все элементы, у которых рейтинг меньше заданного. | Добавить элемент с номером К | |
Структура "Покупатель": - фамилия, имя, отчество; - домашний адрес; - номер телефона; - номер кредитной карточки | Удалить К элементов из начала файла. | Добавить N элементов с номером К | |
Структура "Пациент": - фамилия, имя, отчество; - домашний адрес; - номер медицинской карты; - номер страхового полиса. | Удалить элемент с заданным номером медицинской карты. | Добавить K элементов в начало файла | |
Структура "Информация": - носитель; - объем; - название; - автор. | Удалить первый элемент с заданным объемом информации. | Добавить K элементов в конец файла | |
Структура "DVD-диск": - название фильма; - режиссер; - продолжительность; - цена. | Удалить все элементы с ценой выше заданной. | Добавить элемент с номером К | |
Структура "DVD- диск": - название; - режиссер; - продолжительность; - цена. | Удалить первый элемент с заданной продолжительностью. | Добавить N элементов с номером К | |
Структура "Спортивная команда": - название; - город; - количество игроков; - количество набранных очков. | Удалить все элементы с количеством очков меньше заданного. | Добавить K элементов в начало файла | |
Структура "Стадион": - название; - адрес; - вместимость; - виды спорта. | Удалить элемент с заданным названием. | Добавить K элементов в конец файла | |
Структура "Автомобиль": - марка; - год выпуска; - цена; - цвет. | Удалить все элементы, у которых год выпуска меньше заданного. | Добавить элемент с номером К | |
Структура "Владелец автомобиля": - фамилия, имя, отчество; - номер автомобиля; - телефон; - номер техпаспорта. | Удалить элемент с заданным номером. | Добавить N элементов с номером К | |
Структура "Фильм": - название; - режиссер; - год выпуска; - стоимость. | Удалить все элементы, у которых стоимость превышает заданную. | Добавить K элементов в начало файла | |
Структура "Книга": - название; - автор; - год издания; - количество страниц. | Удалить К элементов из начала файла. | Добавить K элементов в конец файла | |
Структура "Фильм": - название; - режиссер; - страна; - приносимая прибыль. | Удалить К элементов , начиная с номера N из файла. | Добавить элемент с номером К | |
Структура "Государство": - название; - государственный язык; - денежная единица; - курс валюты относительно $. | Удалить элемент с указанным названием. | Добавить N элементов с номером К | |
Структура "Автомобиль": - марка; - серийный номер; - регистрационный номер; - год выпуска. | Удалить все элементы с указанной маркой | Добавить K элементов в начало файла | |
Структура "Владелец автомобиля": - фамилия, имя, отчество; - номер автомобиля; - номер техпаспорта; - отделение регистрации ГАИ. | Удалить элемент с заданным номером. | Добавить K элементов в конец файла | |
1. Структура "Стадион": - название; - год постройки; - количество площадок; - виды спорта. | Удалить все элементы, у которых год постройки меньше заданного. | Добавить элемент с номером К | |
Структура "Студент": - фамилия, имя, отчество; - номер телефона; - группа; - оценки по 3 основным предметам. | Удалить все элементы из группы с указанным номером, у которых среднее арифметическое оценок меньше заданного. | Добавить N элементов с номером К | |
Структура "Студент": - фамилия, имя, отчество; - дата рождения; - домашний адрес; - рейтинг. | Удалить все элементы с указанным рейтингом | Добавить N элементов с номером К |
Содержание отчета
- Постановка задачи (общая и для конкретного варианта).
- Определения функций для реализации поставленных задач.
- Определение функции main().
- Содержимое исходного файла
- Содержимое модифицированного файла.