Лекции Булатицкий Дмитрий Иванович (во многом по материалам Прасолова А. Н.)
Скачать 319.62 Kb.
|
Работа с текстовыми файламиДля работы с текстовым файлом его открывают с параметром открытия файла, содержащим постфикс «t» (впрочем, он подразумевается по умолчанию и, если не написать никакого суффикса типа файла, то файл будет открыт как текстовый). Для работы с текстовыми файлами предназначены функции, аналогичные функциям для работы со стандартным устройством ввода/вывода, и отличаются от них наличием дополнительного аргумента: указателя на дескриптор файла. Вот список основных функций для работы с текстовыми файлами: int fprintf (FILE *stream, const char *template, ...); int fscanf (FILE *stream, const char *template, ...); int fgetc (FILE*); char*fgets (char*, int, FILE*); int fputc (int, FILE*); int fputs (const char*, FILE*); Допустим, у нас есть файл, содержащий такое описание объектов: apples 10 23.4 bananas 5 25.0 bread 1 10.3 Тогда, чтобы считать эти данные, мы можем написать такую программу: #include main () { FILE *file; struct food { char name[20]; unsigned qty; float price; }; struct food shop[10]; char i=0; file = fopen("fscanf.txt", "rt"); while (fscanf (file, "%s%u%f", shop[i].name, &(shop[i].qty), &(shop[i].price)) != EOF) { printf("%s %u %.2f\n", shop[i].name, shop[i].qty, shop[i].price); i++; } } int feof (FILE *stream) The feof function returns nonzero if and only if the end-of-file indicator for the stream stream is set. int fseek( FILE *stream, long offset, int origin ); int _fseeki64( FILE *stream, __int64 offset, int origin ); If successful, fseek and _fseeki64 returns 0. Otherwise, it returns a nonzero value. SEEK_CUR SEEK_END SEEK_SET Для работы с двоичным файлом его открывают с параметром открытия файла, содержащим постфикс «b». Для работы с текстовыми файлами предназначены следующие основные функции: size_t fread(void *буфер, size_t колич_байт, size_t счетчик, FILE *уф); size_t fwrite(const void *буфер, size_t колич_байт, size_t счетчик, FILE *уф); Для fread() буфер — это указатель на область памяти, в которую будут прочитаны данные из файла. А для fwrite() буфер — это указатель на данные, которые будут записаны в файл. Значение счетчик определяет, сколько считывается или записывается элементов данных, причем длина каждого элемента в байтах равна колич_байт. (Вспомните, что тип size_t определяется как одна из разновидностей целого типа без знака.) И, наконец, уф — это указатель файла, то есть на уже открытый поток. Функция fread() возвращает количество прочитанных элементов. Если достигнут конец файла или произошла ошибка, то возвращаемое значение может быть меньше, чем счетчик. А функция fwrite() возвращает количество записанных элементов. Если ошибка не произошла, то возвращаемый результат будет равен значению счетчик. Как только файл открыт для работы с двоичными данными, fread() и fwrite() соответственно могут читать и записывать информацию любого типа. Например, следующая программа записывает в дисковый файл данные типов double, int и long, a затем читает эти данные из того же файла. Обратите внимание, как в этой программе при определении длины каждого типа данных используется функция sizeof(). /* Запись несимвольных данных в дисковый файл и последующее их чтение. */ #include #include int main(void) { FILE *fp; double d = 12.23; int i = 101; long l = 123023L; if((fp=fopen("test", "wb+"))==NULL) { printf("Ошибка при открытии файла.\n"); exit(1); } fwrite(&d, sizeof(double), 1, fp); fwrite(&i, sizeof(int), 1, fp); fwrite(&l, sizeof(long), 1, fp); rewind(fp); fread(&d, sizeof(double), 1, fp); fread(&i, sizeof(int), 1, fp); fread(&l, sizeof(long), 1, fp); printf("%f %d %ld", d, i, l); fclose(fp); return 0; } Как видно из этой программы, в качестве буфера можно использовать (и часто именно так и делают) просто память, в которой размещена переменная. В этой простой программе значения, возвращаемые функциями fread() и fwrite(), игнорируются. Однако на практике эти значения необходимо проверять, чтобы обнаружить ошибки. Одним из самых полезных применений функций fread() и fwrite() является чтение и запись данных пользовательских типов, особенно структур. Например, если определена структура: struct struct_type { float balance; char name[80]; } cust; то следующий оператор записывает содержимое cust в файл, на который указывает fp: fwrite(&cust, sizeof(struct struct_type), 1, fp); В качестве дополнительного примера удобства использования рассмотрим функции для записи массива целых чисел и массива пользовательских структур в файл. // Сохраняет n элементов массива arr в файл с именем fname. Файл очищается // перед сохранением данных. // Функция возвращает количество удачно записанных элементов массива int SaveArrayToBinFile(int *arr, int n, char const *fname) { FILE *fp; fp=fopen(fname, "wb"); if(fp==NULL) return 0; return fwrite(arr, sizeof(arr[0]), n, fp); } // Сохраняет n элементов массива arr в файл с именем fname. Файл очищается // перед сохранением данных. // Функция возвращает количество удачно записанных элементов массива int SaveArrayToBinFile(MyStruct *arr, int n, char const *fname) { FILE *fp; fp=fopen(fname, "wb"); if(fp==NULL) return 0; return fwrite(arr, sizeof(arr[0]), n, fp); } Загрузка из подобных файлов может осуществляться как поэлементно, так и целиком: // Загружает n элементов в буфер arr из файла с именем fname. // Функция возвращает количество удачно прочитанных элементов массива int LoadArrayFromBinFile(int *arr, int n, char const *fname) { FILE *fp; fp=fopen(fname, "rb"); if(fp==NULL) return 0; return fread(arr, sizeof(arr[0]), n, fp); } // Загружает n элементов в буфер arr из файла с именем fname. // Функция возвращает количество удачно прочитанных элементов массива int LoadArrayFromBinFile(MyStruct *arr, int n, char const *fname) { FILE *fp; fp=fopen(fname, "wb"); if(fp==NULL) return 0; return fwrite(arr, sizeof(arr[0]), n, fp); } Преимуществом двоичной работы с файлами является не только удобство работы и компактность файла, но и эффективность: ведь не приходится тратить время на преобразование из внутреннего представления данных в текстовый вид и обратно. |