Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке? Тип приложения - не Qt, Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.
|
Организация ввода-вывода в C++
7.3 Обработка двоичных файлов
Если в файле хранятся только числа или данные определённой структуры, то для хранения таких значений удобно пользоваться двоичными файлами. В двоичных файлах информация считывается и записывается в виде блоков определённого размера, в них могут храниться данные любого вида и структуры.
Порядок работы с двоичными и текстовыми файлами аналогичен. Для того, чтобы записать данные в двоичный файл, необходимо:
- Описать файловую переменную с помощью оператора FILE *filename; Здесь filename — имя переменной, где будет храниться указатель на файл.
- Открыть файл с помощью функции fopen.
- Записать информацию в файл с помощью функции fwrite.
- Закрыть файл с помощью функции fclose.
Для того, чтобы считывать данные из двоичного файла, необходимо:
- Описать переменную типа FILE *.
- Открыть файл с помощью функции fopen.
- Считать необходимую информацию из файла с помощью функции fread, при считывании информации следить за тем, достигнут ли конец файла.
- Закрыть файл с помощью функции fclose.
Рассмотрим основные функции, необходимые для работы с двоичными файлами.
Для открытия файла предназначена функция:
FILE *fopen(const *filename, const char *mode); где, filename — строка, в которой хранится полное имя открываемого файла, mode — строка, которая определяет режим работы с файлом; возможны следующие значения:
- "rb" — открыть двоичный файл в режиме чтения;
- "wb" — создать двоичный файл для записи, если файл существует, то его содержимое очищается.
- "ab" — создать или открыть двоичный файл для добавления информации в конец файла;
- "rb+" — открыть существующий двоичный файл в режиме чтения и записи;
- "wb+" — открыть двоичный файл в режиме чтения и записи, существующий файл очищается;
- "ab+" — двоичный файл открывается или создаётся для исправления существующей информации и добавления новой в конец файла.
Функция fopen возвращает в файловой переменной NULL в случае неудачного открытия файла.
После открытия файла, в указателе содержится адрес 0-го байта файла. По мере чтения или записи значение указателя смещается на считанное (записанное) количество байт. Текущее значение указателя — номер байта, начиная с которого будет происходить операция чтения или записи.
Для закрытия файла предназначена функция
int fclose(FILE *filename);
Она возвращает 0 при успешном закрытии файла и NULL в противном случае.
Для удаления файлов существует функция
int remove(const char *filename);
Эта функция удаляет с диска файл с именем filename. Удаляемый файл должен быть закрыт. Функция возвращает ненулевое значение, если файл не удалось удалить.
Для переименования файлов предназначена функция
int rename(const char *oldfilename, const char *newfilename);
здесь первый параметр — старое имя файла, второй — новое. Возвращает 0 при удачном завершении программы.
Чтение из двоичного файла осуществляется с помощью функции
fread (void *ptr, size, n, FILE *filename)
Эта функция считывает из файла filename в массив ptr n элементов размера size. Функция возвращает количество считанных элементов. После чтения из файла указатель файла смещается на n*size байт.
Запись в двоичный файл осуществляется с помощью функции
fwrite (const void *ptr, size, n, FILE *filename);
Функция записывает в файл filename из массива ptr n элементов размера size. Функция возвращает количество записанных элементов. После записи информации в файл, указатель файла смещается на n*size байт.
Для контроля достижения конца файла есть функция
int feof(FILE * filename);
Она возвращает ненулевое значение, если достигнут конец файла.
Рассмотрим использование двоичных файлов на примере решения двух стандартных задач.
Задача 7.4.Создать двоичный файл , куда записать целое число и вещественных чисел.
#include <iostream> #include <fstream> using namespace std; int main ( ) { FILE * f; //Описание файловой переменной. int i, n; double a; f=fopen ( " abc.dat ", " wb " ); //Создание двоичного файла в режиме записи. cout<<" n = "; cin>>n; //Ввод числа n. fwrite (&n, sizeof ( int ), 1, f ); //Запись числа в двоичный файл. for ( i =0; i<n; i++) //Цикл для ввода n вещественных чисел. { cout<<" a = "; cin>>a; //Ввод очередного вещественного числа. fwrite (&a, sizeof ( double ), 1, f ); //Запись числа в двоичный файл. } fclose ( f ); //Закрыть файл. return 0; }
Задача 7.5. Вывести на экран содержимое созданного в задаче 7.4 двоичного файла abc.dat.
#include <iostream> #include <fstream> using namespace std; int main ( ) { FILE * f; //Описание файловой переменной. int i, n; double *a; f=fopen ( " abc.dat ", " rb " ); //Открыть существующий двоичный файл в режиме чтения. fread (&n, sizeof ( int ), 1, f ); //Читать из файла целое число в переменную n. cout<<" n = "<<n<<" \n "; //Вывод n на экран. a=new double [ n ]; //Выделение памяти для массива из n чисел. fread ( a, sizeof ( double ), n, f ); //Чтение n вещественных чисел из файла в массив a. for ( i =0; i<n; cout<<a [ i ]<<" \t ", i++); //Вывод массива на экран. cout<<endl; fclose ( f ); //Закрыть файл. return 0; }
Двоичный файл — последовательная структура данных, после открытия файла доступен первый байт. Можно последовательно считывать из файла или записывать их в него. Допустим, необходимо считать пятнадцатое, а затем первое число, хранящееся в файле. С помощью последовательного доступа это можно сделать следующим образом.
FILE * f; int i, n; double a; f=fopen ( " file.dat ", " rb " ); for ( i =0; i <15; fread (&a, sizeof ( double ), 1, f ), i++); fclose ( f ); f=fopen ( " file.dat ", " rb " ); fread (&a, sizeof ( double ), 1, f ); fclose ( f );
Как видно, такое чтение чисел из файла, а затем повторное открытие файла — не самый удачный способ. Гораздо удобнее использовать функцию перемещения указателя файла к заданному байту:
int fseek(FILE *F, long int offset, int origin);
Функция устанавливает указатель текущей позиции файла F, в соответствии со значениями начала отсчёта origin и смешения offset. Параметр offset равен количеству байтов, на которые будет смещён указатель файла относительно начала отсчёта, заданного параметром origin. Если значение offset положительно, то указатель файла смещается вперёд, если отрицательно — назад. Параметр origin должен принимать одно из следующих значений, определённых в заголовке stdio.h:
- SEEK_SET— отсчёт смещения offset вести с начала файла;
- SEEK_CUR — отсчёт смещения offset вести с текущей позиции файла;
- SEEK_END — отсчёт смещения offset вести с конца файла.
Функция возвращает нулевое значение при успешном выполнении операции и ненулевое, если возник сбой при выполнении смещения.
Функция fseek фактически реализует прямой доступ к любому значению в файле. Необходимо только знать месторасположение (номер байта) значения в файле. Рассмотрим использование прямого доступа в двоичных файлах на примере решения следующей задачи.
Задача 7.6. В созданном в задаче 7.4 двоичном файле поменять местами наибольшее и наименьшее из вещественных чисел.
Алгоритм решения задачи состоит из следующих этапов:
- Чтение вещественных чисел из файла в массив a.
- Поиск в массиве a максимального (max) и минимального (min) значения и их номеров (imax, imin).
- Перемещение указателя файла к максимальному значению и запись min.
- Перемещение указателя файла к минимальному значению и запись max.
Ниже приведён текст программы решения задачи с комментариями.
#include <iostream> #include <fstream> using namespace std; int main ( ) { FILE * f; //Описание файловой переменной. int i, n, imax, imin; double *a, max, min; f=fopen ( " abc.dat ", " rb + " ); //Открыть файл в режиме чтения и записи. fread (&n, sizeof ( int ), 1, f ); //Считать из файла в переменную n количество элементов в файле. a=new double [ n ]; //Выделить память для хранения вещественных чисел, //эти числа будут хранится в массиве a. fread ( a, sizeof ( double ), n, f ); //Считать из файла в массив a вещественные числа. //Поиск максимального, минимального элемента в массиве a, и их индексов. for ( imax=imin =0, max=min=a [ 0 ], i =1; i<n; i++) { if ( a [ i ]>max) { max=a [ i ]; imax= i; } if ( a [ i ]<min ) { min=a [ i ]; imin= i; } } //Перемещение указателя к максимальному элементу. fseek ( f, sizeof ( int )+imax* sizeof ( double ),SEEK_SET); //Запись min вместо максимального элемента файла. fwrite (&min, sizeof ( double ), 1, f ); //Перемещение указателя к минимальному элементу. fseek ( f, sizeof ( int )+imin * sizeof ( double ),SEEK_SET); //Запись max вместо минимального элемента файла. fwrite (&max, sizeof ( double ), 1, f ); //Закрытие файла. fclose ( f ); //Освобождение памяти, выделенной под массив a. delete [ ] a; return 0; }