Обработка файлов на C

Файлы делятся на две категории:

  • текстовые;
  • двоичные.

В текстовом файле содержатся символы ASCII, а каждая строка заканчивается специальным символом, которым в каждой операционной системе определяется конец строки. Например, в Unix это символ \n.

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

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

Наконец, в двоичном файле чтение и запись быстрее.

Открытие файла

Файл открывается функцией fopen():

FILE *fopen(const char *filename, const char *mode)

// с помощью filename объявляется имя файла
// с помощью mode объявляется выполняемое действие (см. таблицу ниже)
Режимы доступа fopen()

Когда файл для редактирования находится в одной папке с исполняемым файлом, просто включаем filename, когда они в разных папках  —  задаем в качестве filename путь к файлу:

// файл в одной папке с исполняемым
fopen("test_file.txt", r);

// файл находится в другом каталоге
fopen("C:/boot/grub/gfxblacklist.txt", a+);

Рекомендуется всегда проверять возвращаемое в fopen() значение. Файл редактируется, только когда значение не NULL:

if ((check = fopen(filename, 'r')) == NULL)

Редактирование файла

  • fseek();
  • ftell();
  • fputs();
  • fprintf();
  • putc();
  • fscanf();
  • fgets();
  • getc();
  • fread();
  • fwrite().

fseek()

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

После каждого редактирования позиция индекса обновляется автоматически. Индекс перемещается функцией fseek():

int fseek(FILE *indx, long int offset, int origin);

/*в fseek индекс перемещается от параметра исходного положения origin на число байтов, указанное в indx параметром смещения offset*/
// смещение бывает и отрицательным, тогда индекс перемещается «назад»

Параметром offset определяется исходное положение перемещаемого индекса:

Константа | значениеописание

Если индекс перемещен, в fseek() возвращается 0, если нет  —  целое число, отличное от нуля:

fseek(indx, 0, SEEK_END);
// Перемещение в конец файла
fseek(indx, 0, SEEK_SET);
// Перемещение в начало файла
fseek(indx, 44, SEEK_CUR);
// Перемещение на 44 байта вперед от текущей позиции
fseek(indx, -7, SEEK_CUR);
// Перемещение на 7 байт назад от текущей позиции

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


ftell()

Функцией ftell() определяется текущее положение индекса. Ею возвращается число байтов от начала файла до индекса:

long int ftell(FILE *indx);

fputs()

Функцией fputs() в текстовый файл добавляется строка:

int fputs(const char *str, FILE *fptr);
/*параметр str — это указатель на
добавляемую в файл строку*/

Например:

fptr = fopen("test_file.txt", 'w');
fputs ("Hello World!\n" , fptr);

fprintf()

Функцией fprintf() содержимое выводится в файле, в отличие от printf() и ее стандартного вывода на экран stdout.

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

int fprintf(FILE *fptr, const char *format, ...);

Например:

FILE* fptr;
int i;
fptr = fopen("test_file.txt", 'w');
for (i = 0; i < 5; i++)
fprintf(fptr, "%d %s\n", i , "Hello World!");

putc()

Функцией putc() в файл добавляется символ. Если редактирование успешно, в функции возвращается записанный в файле символ, если нет  —  конец файла EOF:

int putc(int ch, FILE *fptr);
// ch — это символ, записываемый в файле, указанном в fp

В примерах выше, если вместо параметра fp задать stdout, результаты выведутся на экран.


fscanf()

Функцией fscanf() в текстовом файле сканируются различные данные. В двоичных файлах это тоже возможно, но не рекомендуется. В fscanf() данные считываются из файла, в отличие от scanf() с ее приемом ввода из stdin.

Возвращается число считанных элементов, присвоенных переменным. Если fscanf() задействуется в конце файла или случаются любые другие проблемы, в функции возвращается EOF:

int fscanf(FILE *fp, const char *format, ...);

fgets()

Функцией fgets() символы в файле считываются до специального символа новой строки или последнего символа файла.

Если fgets() выполняется успешно, символы сохраняются в переменной с указателем и возвращается этот указатель, если нет  —  возвращается NULL:

char *fgets(char *str, int size, FILE *fptr);
// str — это указатель на то, где сохранятся символы
// Параметром size определяется количество считываемых символов

getc()

Функцией getc() в файле считывается один символ. Если getc() выполняется успешно, возвращается считанный символ, если нет  —  EOF:

int getc(FILE *fptr);

Возвращаемое значение должно сохраняться в переменной int, а не char.


fwrite()

Функцией fwrite() в файле записываются большие объемы данных:

int fwrite(const void *data, size_t size, size_t count, FILE *fptr);

/*data — это указатель на адрес памяти
добавляемых в файл данных;
чтобы сохранять данные любого типа, он объявлен как void*/
// данные сохраняются в файле, обозначаемом указателем fptr
// параметром count указывается число сохраняемых элементов
// параметром size указывается размер каждого элемента в байтах

Например:

// сохранение в файле массива 44 элементов
int array[44];
fwrite(array, sizeof(int), 44, fptr);

fread()

Функцией fread() считываются большие объемы данных. Возвращается число считанных из загруженного файла элементов.

Если это значение не равно параметру count в примере ниже, значит, при загрузке файла произошла ошибка или мы добрались до конца файла:

int fread(void *data, size_t size, size_t, count FILE *fptr);

/*data — это указатель на адрес памяти переменной, в которой сохранятся считываемые из файла данные;
чтобы сохранять данные любого типа, он объявлен как void*/
// параметром count указывается число считываемых элементов
// параметром size указывается размер каждого элемента в байтах

Например:

// считывание в файле массива 700 элементов
int array[700];
fread(array, sizeof(int), 700, fptr);

feof()

Этой функцией определяется, добрались ли мы до конца файла. Если в функции считывания данные считываются после окончания файла, в feof() возвращается ненулевое значение, если до  —  0:

int feof(FILE *fptr);

Например:

FILE *fptr;
char array[40];

fptr = fopen("test_file.txt", 'r');
// предполагается, что файл успешно загружен
// так мы не проверяем, не NULL ли значение fptr

if (fgets(array, sizeof(str), fptr) != NULL)
print("%s", array);
else
{
if(feof(fptr))
printf("End of file\n");
else
printf("Failed for another reason\n");
}

Закрытие файла

Файл закрывается функцией fclose():

int flcose(FILE *fptr);

// fptr нужен для указания файла
/*функция объявлена как int: если файл закрывается, в ней возвращается 0, если нет — EOF*/

Хотя при завершении программы файл закрывается автоматически, все равно рекомендуется использовать функцию fclose(): в случае нежелательного или аварийного завершения данные останутся невредимыми.

Вот и все. Это была обработка файлов на C. Рекомендую ознакомиться с каждой функцией чуть подробнее: в одной статье всего не разберешь.

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Alopix | Αλώπηξ: C:File Handling

Предыдущая статьяОсновы Android-разработки в Revolut
Следующая статьяFastAPI, Flask или Streamlit: что выбрать для веб-разработки?