Работа с файлами
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Лекция 6
Работа с файлами
Я могу вас поздравить! Если вы выполнили лабораторную 5, особенно с дополнительным
заданием, вы теперь знаете самые азы языка! В дальнейшем мы лишь будем углубляться в
его особенности, но чтобы писать базовые программы, у вас достаточно знаний. Пора нам
узнать, как сохранять выходные данные на период дольше, чем работает программа.
Вы прекрасно знаете, что для долговременного хранения данных используются файлы.
Работа с файлами в Си не так уж сложна, в действительности, это очень простая тема, если
вы разобрались со строками.
Файл представляет собой набор байт, иногда он интерпретируется, как набор строк. Т.е. для
сохранения информации вы записываете набор строк в файл, и затем считываете этот набор,
если сохраненная информация понадобилась снова.
Давайте разберем подробно все шаги работы с файлами:
1. Файл необходимо создать, если он уже был создан, то необходимо открыть. Это
важный шаг, потому что при открытии файла операционная систем блокирует к нему
доступ для других программ. Все дело в том, что если бы все программы могли
одновременно сохранять информацию в один и тот же файл, была бы полная
неразбериха. Но с другой стороны, если информация только читается, то почему
доступ к файлу нужно ограничивать? Поэтому то и создано 2 способа открытия
файла: на чтение и на запись. При открытии файла любым из этих способов, вы
получаете идентификатор для работы с этим файлом. Поэтому программе его нужно
запомнить.
2. Сама работа с файлом. Данные либо считываются, либо записываются.
3. Закрытие файла. Программа сообщает операционной системе, что больше с файлом не
работает, и он может быть доступен для ранее неразрешенных действий. (Например,
программа, читающая файл, заблокирует его для удаления, поэтому файлы важно
закрывать в любом случае)
Теперь посмотрим, что нужно написать в коде:
#include для начала
Далее используется функция fopen
FILE * fopen( const char * fname, const char * modeopen );
Пример:
FILE * fo = fopen("test.txt","wt"); //открывает файл на запись
Можно задать «абсолютный» путь к файлу с указанием диска:
fo = fopen("c:\\tmp\\test.txt","wt");
Но я не советую привыкать к такому коду, он не является переносимым. Ваша программа
будет переставать работать с большой вероятностью, если вы ее принесете на другой
компьютер.
В случае с «относительным» путем файл будет искаться, начиная с папки, где находится
исполняемый файл (ехе — файл)
Например, вы сохранили exe — файл в папке C:\\Programs
Тогда файл text.txt будет искаться там, но если вы перенесете exe – файл в другую папку, то
он будет искаться уже в новой папке. Этим относительные пути удобнее. Естественно, что в
относительных путях могут быть каталоги, например ”save\\test.txt”. В этом случае
программа будет искать папку save рядом с exe - файлом, а уже в ней будет искать файл
test.txt.
Modeopen - Строка, содержащая режим доступа к файлу.
«r»
Режим открытия файла для чтения. Файл должен существовать.
«w» Режим создания пустого файла для записи. Если файл с таким именем уже существует
его содержимое стирается, и файл рассматривается как новый пустой файл.
«a»
Дописать в файл. Операция добавления данных в конец файла. Файл создается, если
он не существует.
«r+» Режим открытия файла для обновления чтения и записи. Этот файл должен
существовать.
«w+» Создаёт пустой файл для чтения и записи. Если файл с таким именем уже существует
его содержимое стирается, и файл рассматривается как новый пустой файл.
«a+» Открыть файл для чтения и добавления данных. Все операции записи выполняются в
конец файла, защищая предыдущее содержания файла от случайного изменения. Вы можете
изменить позицию (FSEEK, перемотка назад) внутреннего указателя на любое место файла
только для чтения, операции записи будет перемещать указатель в конец файла, и только
после этого дописывать новую информацию. Файл создается, если он не существует.
Если вы открываете текстовый файл, то следует добавить букву t в режим доступа, если
бинарный, то b
Примеры:
rt+ Режим открытия текстового файла для обновления чтения и записи.
wb+ Создаёт пустой бинарный файл для чтения и записи.
wt Создаёт пустой текстовый файл для чтения и записи.
rb Режим открытия бинарного файла для чтения.
И т. д.
Закрывается файл функцией fclose. В нее нужно передать «дескриптор файла», это то
значение, что сохранилось в переменную при открытии:
fclose(fo);
Теперь самое интересное. Для записи в файл может использоваться старая знакомая функция
printf, только она будет выглядеть теперь так:
fprintf( fo, "Привет!" );
По аналогии можно использовать fputs.
Например, следующая программа сохраняет в файл числа от 0 до 100, каждое число пишется
с новой строки
FILE * fo = fopen("test.txt","wt");
for(int i = 0; i < 100; ++i)
{
fprintf( fo, "%i\n", i );
}
fclose(fo);
Для чтения данных используется fscanf или fgets.
FILE * fi = fopen("test.txt","rt");
int n;
while(!feof(fi))
{
fscanf(fi, "%i", &n);
}
fclose(fi);
Считает последовательно вам обратно эти числа. Функция feof (сокращенно end of file)
возвратит true, когда он кончится. Запомните шаблоны записи и чтения, они мало чем будут
меняться.
Но это все для текстовых файлов, как же с бинарными? В Си есть еще 2 функции, которые
запишут в файл все что угодно. Это fwrite и fread
size_t fwrite( const void * ptrvoid, size_t size, size_t count, FILE * filestream );
Функция fwrite записывает массив размером — count элементов, каждый из которых имеет
размер size байт, в блок памяти, на который указывает ptrvoid — текущая позиция в потоке.
Индикатор положения потока увеличивается на общее число записанных байт.
Общее количество записанных байт (count*).
Звучит сложно?
Разберем все параметры отдельно:
ptrvoid — Указатель на массив элементов, которые необходимо записать в файл.
size — Размер в байтах каждого элемента массива.
count — Количество элементов, каждый из которых занимает size байт.
filestream — Дескриптор файла.
Возвращаемое значение — общее число элементов, которые успешно были записаны.
Возвращаемое значение, в этом случае имеет тип данных size_t. Если возвращаемое значение
отличается от количества элементов, значит произошла ошибка.
int main ()
{
FILE * ptrFile = fopen ( "file.txt" , "wb" );
char buffer[] = { 'w' , 't' , 'f' };
fwrite(buffer , 1 , sizeof(buffer) , ptrFile );
fclose (ptrFile);
return 0;
}
Функция fread работает аналогичным образом, только считывает данные:
int main()
{
FILE * ptrFile = fopen( "file.txt" , "rb" );
if (!ptrFile)
{
fputs("file not found", stderr);
return 1;
}
char buffer[256];
fseek(ptrFile , 0 , SEEK_END);
// устанавливаем позицию в конец файла
size_t lSize = (size_t)ftell(ptrFile); // получаем размер в байтах
rewind (ptrFile);
// устанавливаем позицию в начало файла
size_t result = fread(buffer, 1, lSize, ptrFile); // считываем файл в буфер
if (result != lSize)
{
fputs("reading error", stderr);
return 2;
}
//содержимое файла теперь находится в буфере
puts(buffer);
// завершение работы
fclose (ptrFile);
return 0;
}
Чтение немного сложнее, но оно и понятно: во-первых, мы понятия не имеем сколько
информации нам надо считать. Мы это вычисляем при помощи новых функций. Во-вторых,
для чтения нужно восстановить позицию в начало файла, и только потом считать. Также
здесь приведена пара проверок, на стандартные ошибки, их нужно использовать в
лабораторной.
В файлах открытых для обновления (например, открытые для чтения и записи), поток
должен быть обнулён после выхода, прежде чем выполнять операции ввода. Это может быть
сделано либо путем позиционирования (fseek, fsetpos, rewind) или явно, вызвав функцию
fflush, как в этом примере:
int main()
{
char buffer[80];
FILE * ptrFile = fopen("example.txt", "r+");
if (!ptrFile)
fputs("file error", stderr);
else
{
fputs ("тест", ptrFile);
fflush (ptrFile);
fgets(buffer, 80, ptrFile);
puts (buffer);
fclose (ptrFile);
return 0;
// очистка потока
}
}
fflush может использоваться также и для очистки потоков ввода и вывода, но не на каждой
операционной системе поток ввода гарантированно очистится (для Windows, как правило,
работает). Поток ввода называется stdin, поток вывода — stdout.
Лабораторная работа №6
Задание 0.
Записать в файл текст, введенный с консоли, в обратном порядке. Считать текст и вывести на
экран в обратном порядке. (В итоге получится тот текст, который и вводили)
Задание 1.
Реализовать простейший редактор текста. Он должен иметь меню, состоящее из 4 пунктов:
1. Создать новый файл.
2. Добавить в существующий.
3. Открыть файл для чтения.
4. Выход.
При выборе 1 и 2 пункта пользователю предлагается ввести текст, когда он закончит ввод
клавишей Enter, будет предложение продолжить ввод или сохранить в файл. Имя файла
пользователь должен ввести вручную. После сохранения файла пользователь попадает в
меню.
При выборе 3 пункта, пользователю показывается на экран содержимое файла, после
нажатия enter, пользователь попадает в меню.
При выборе 4 пункта программа завершается.
Задание 2.
Программа должна считывать слова из файла, затем сортировать в лексикографическом
порядке и записывать в выходной файл.