Файлы делятся на две категории:
- текстовые;
- двоичные.
В текстовом файле содержатся символы ASCII, а каждая строка заканчивается специальным символом, которым в каждой операционной системе определяется конец строки. Например, в Unix это символ \n
.
Двоичные файлы состоят из нечитаемого содержимого и не разбиваются на строки, поэтому необходимости в специальных символах нет.
Двоичные файлы обычно меньше текстовых по размеру: в последних для представления содержимого всегда используются символы ASCII, а в двоичных файлах — количество байтов, необходимое для каждого элемента содержимого.
Наконец, в двоичном файле чтение и запись быстрее.
Открытие файла
Файл открывается функцией fopen()
:
FILE *fopen(const char *filename, const char *mode)
// с помощью filename объявляется имя файла
// с помощью mode объявляется выполняемое действие (см. таблицу ниже)
Когда файл для редактирования находится в одной папке с исполняемым файлом, просто включаем 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. Рекомендую ознакомиться с каждой функцией чуть подробнее: в одной статье всего не разберешь.
Файлы предполагают разнообразные практические упражнения и идеи для начинающих, с ними возможно сохранение выходных данных программ и при необходимости выполнение нескольких операций.
Читайте также:
- Язык C: операторы
- Спецификатор constexpr в C++: зачем он нужен и как работает
- Экспериментируем с битбордом и битовыми операциями в Unreal 4
Читайте нас в Telegram, VK и Дзен
Перевод статьи Alopix | Αλώπηξ: C:File Handling