А.А. Белеванцев, С.С. Гайсарян, Л.С. Корухова, Е.А. Кузьменкова, В.С. Махнычев. Семинары по курсу Алгоритмы и алгоритмические языки (1108027), страница 12
Текст из файла (страница 12)
Бинарные файлыПри работе с бинарными данными в файле функции fgets/fputs,fscanf/fprintf использовать невозможно, поскольку они интерпретируютсодержимое файла как текст — то есть последовательность строковых данных. Двоичныеже данные записываются ровно в том виде, в котором они представляются в памяти.Например, на архитектуре little-endian с 32-битным целым типом запись однойпеременной целого типа в файл должна поместить в файл 4 байта, находящиеся в памятипо адресу, где хранится переменная: первым запишется младший (нулевой) байт,последним — старший байт. Аналогично, при чтении последовательные байты файлаинициализируют последовательные байты в памяти, поэтому на одной и той жеархитектуре считанный объект некоторого типа будет равен предварительно записанномув это место файла объекту того же типа.Если в файле записана последовательность байт с кодами 0x31 0x32 0x33 0x34,то как текстовый в кодировке ASCII он будет считан как строка "1234".
Функция fscanfс форматным преобразованием "%d" считает число 1234. В двоичном же виде должнобыть считано число 0x3433323116 = 87577041710.Для чтения и записи в/из файлов данных в двоичном виде используются функцииfread и fwrite.Функция fwrite объявлена в стандартной библиотеке Си какsize_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp);Функция записывает в файл, на который указывает дескриптор fp, nmembобъектов, каждый из которых имеет размер size, и берет эти объекты последовательнопо адресу ptr. Можно представлять себе, что ptr должен указывать на массив из nmembэлементов, в котором хранятся объекты размера size.
Функция возвращает количествоуспешно записанных элементов, что обычно совпадает с nmemb.Для записи массива из 10 целых элементов:int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9};FILE *f = fopen ("out.bin", "wb");fwrite (a, sizeof (int), 10, f);или:fwrite (a, sizeof (a), 1, f);Для записи одного числа double или структуры:54structstructfwritefwritepoint { int x, y; };point pt = {3, 3}, *p = &pt; double d;(p, sizeof (struct point), 1, f);(&d, sizeof (d), 1, f);Обратите внимание, что из-за необходимости передавать адреса любых объектовпервый параметр fwrite имеет тип const void *.Функция fread имеет тот же прототип, но на этот раз первый аргумент ptrуказывает на область памяти, в которую будут последовательно считаны nmemb объектов,каждый из которых имеет размер size. Аналогично, возвращается количество успешносчитанных объектов.
Если был достигнут конец файла или произошла другая ошибка, товозвращаемое значение будет меньше nmemb. Используйте этот факт для проверкикорректности ввода.Для считывания назад записанного массива a или числа d:FILE *f = fopen ("out.bin", "rb");fread (a, sizeof (int), 10, f);fread (&d, sizeof (double), 1, f);9.5. Позиционирование в файлеФункция fseek устанавливает текущую позицию в файле:int fseek(FILE *stream, long offset, int origin);Новое значение текущей позиции, измеряемое в символах (байтах), получаетсядобавлением offset байтов к позиции, указанной параметром origin.
Возможныезначения origin: SEEK_SET – смещение отсчитывается относительно начала файла (параметрoffset должен быть неотрицателен); SEEK_CUR – смещение отсчитывается относительно текущей позиции в файле(параметр offset может иметь любой знак); SEEK_END – смещение отсчитывается относительно конца файла (offset долженбыть меньше либо равен нуля).Константы SEEK_SET, SEEK_CUR, SEEK_END также описаны в stdio.h. Вслучае успешного завершения fseek возвращает 0, в противном случае – -1.Узнать значение текущей позиции в файле можно при помощи функции ftell:long ftell(FILE *stream);Функция возвращает значение текущей позиции (смещение в байтах относительноначала файла) или -1 в случае ошибки.Пример 2.
Определить и напечатать размер файла input.txt.FILE *f;long size;f = fopen ("input.txt", "r");if (f != NULL) {55fseek (f, 0, SEEK_END);size = ftell (f);fclose (f);printf ("Размер файла - %ld байт.\n", size);} else {printf ("Не удалось открыть файл input.txt\n");}Задача 1. Файл a.txt содержит два целых числа. Требуется дописать их сумму в конецэтого же файла.#include <stdio.h>int main (void) {FILE *f;int a, b;f = fopen ("a.txt", "r+");fscanf (f, "%d%d", &a, &b);fprintf (" %d", a+b);fclose (f);return 0;}Задача 2.
Требовалось описать функцию, которая для заданного файла, уже открытого начтение, подсчитывает количество десятичных цифр в нем. Какие из приведенных нижеописаний правильно решают эту задачу?а)int digits (FILE *f) {unsigned char c;int result = 0;while ((c = fgetc (f)) != EOF) {if (c >= '0' && c <= '9') result++;}return result;}Цикл while в данной функции никогда не завершится: при достижении концафайла значение константы EOF будет приведено к типу unsigned char в моментприсваивания c = fgetc (f), и поскольку эта константа отрицательна, то такое послеприведения ее знак изменится, и последующее сравнение с исходным значением EOFбудет ложно.б)int digits (FILE *f) {signed char c;int result = 0;while ((c = fgetc (f)) != EOF) {if (c >= '0' && c <= '9') result++;}return result;}Описание этой функции также ошибочно. Константа EOF не принадлежит типуchar (ее тип – int).
В большинстве реализаций значение EOF равно -1, и цикл успешно56завершится по достижении конца файла. Однако он завершится и при чтении символа, кодкоторого при приведении к типу signed char окажется равным -1. Например, приобработке файла в кодировке Windows-1251 первая же буква "я" (имеющая код 255)завершит работу такого цикла (поскольку (signed char) 255 = -1).в)int digits (FILE *f) {int c;int result = 0;while ((c = fgetc (f)) != EOF) {if (c >= '0' && c <= '9') result++;}return result;}Задача решена верно.9.6.
Задачи для самостоятельного решенияВ задачах 9.6.1.— 9.6.9. требуется написать полную программу, обрабатывающуютекстовый файл с именем input.txt.9.6.1. Определить, сколько раз в файле встречается последовательность символовfor.9.6.2. Распечатать все строки файла, содержащие заданную строку (вводится склавиатуры) в качестве подстроки. Известно, что длина строки в файле не превосходит 80символов.9.6.3. Определить, какая строка является самой длинной в файле.
Если таких строкнесколько, то выдать первую из них.9.6.4. В файле записана непустая последовательность целых чисел. Требуется:а) найти наибольшее из этих чисел;б) подсчитать количество четных чисел;в) определить, составляют ли эти числа арифметическую прогрессию;г) определить, составляют ли эти числа геометрическую прогрессию;д) определить, сколько чисел этой последовательности являются точнымиквадратами.9.6.5.
Дописать в конец файла строку FINISH.9.6.6. Создать файл с именем copy.txt – копию заданного файла.9.6.7. Удалить из файла все пустые строки.9.6.8. В файле записана непустая последовательность целых чисел, являющихсячислами Фибоначчи. Приписать еще одно, очередное число Фибоначчи.9.6.9. Изменить файл следующим образом: перед каждой строкой добавить ееномер и пробел. Длина строки в исходном файле не превосходит 80 символов.9.6.10. Файлы a.txt и b.txt содержат последовательности целых чисел(возможно, пустые), причем числа в каждом файле упорядочены по неубыванию.Написать программу, которая создает файл c.txt, содержащий все числа из обоих57входных файлов и также упорядоченный по неубыванию. Запрещается использоватьмассивы и динамическую память.10. Динамические структуры данныхДинамические структуры данных используются для представления в памятиданных, размер которых заранее неизвестен.
В дальнейшем будут рассматриватьсядинамические структуры данных, состоящие из однотипных элементов (звеньев),некоторые из которых содержат ссылки на другие звенья.При реализации таких конструкций на языке Си для представления звеньевиспользуются структуры (struct), а для представления ссылок – указатели. Память подзвенья выделяется динамически (например, функцией malloc) и должна освобождаться(функцией free) при удалении звеньев.10.1.
СпискиЛинейный однонаправленный список – структура данных, в которой каждое звеносодержит ссылку на следующее звено. Например, можно привести такое описание списка,каждое звено которого хранит целое число в качестве данных:struct listnode {int elem; // хранимое значениеstruct listnode *next; // указатель на следующее звено};Последнее звено списка содержит в поле next значение NULL (для некольцевыхсписков) или указатель на первое звено списка (для кольцевых списков).В звеньях двунаправленного списка хранятся ссылки не только на следующее звено,но и на предыдущее:struct listnode {int elem; // хранимое значениеstruct listnode *prev, *next; // предыдущее и следующее звено};Для работы со списком достаточно указателя на его первое звено: все остальныезвенья можно получить, последовательно просматривая список.
Поэтому при передачесписка в качестве параметра передают только этот указатель. Для списка, не содержащегони одного звена, значение такого указателя принимают равным NULL.Пример 1. Описана переменнаяstruct listnode *L;Построить список из трех звеньев, содержащий числа 1, 2, 3, и занести в Lуказатель на его начало.struct listnode *p;// создание первого звена спискаp = (listnode*) malloc (sizeof (struct listnode));p->elem = 1;58// создание второго звена спискаp->next = (listnode*) malloc (sizeof (struct listnode));p->next->elem = 2;L = p;// сдвиг p на второе звено спискаp = p->next;// создание последнего звена спискаp->next = (listnode*) malloc (sizeof (struct listnode));p->next->elem = 3;p->next->next = NULL; // следующего звена нетПример 2.