«

»

C++. Обработка двоичных файлов

При записи информации в двоичный файл символы и числа записываются в виде последовательности байт.

Для того чтобы записать данные в двоичный файл, необходимо:

  1. описать файловую переменную типа FAIL * с помощью оператора FILE *filename, здесь filename — имя переменной, где будет храниться указатель на файл.
  2. открыть файл с помощью функции fopen
  3. записать информацию в файл с помощью функции fwrite
  4. закрыть файл с помощью функции fclose

Для того чтобы считать данные из двоичного файла, необходимо:

  1. описать переменную типа FILE *
  2. открыть файл с помощью функции fopen
  3. считать необходимую информацию из файла с помощью функции fread, при этом следить за тем достигнут ли конец файла.
  4. закрыть файл с помощью функции fclose

Рассмотрим основные функции, необходимые для работы с двоичными файлами.

Для открытия файла предназначена функция fopen.

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

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

  • «rb» — открываем двоичный файл в режиме чтения;
  • «wb» — создаем двоичный файл для записи; если он существует, то его содержимое очищается;
  • «ab» — создаем или открываем двоичный файл для дозаписи в конец файла;
  • «rb+» — открываем существующий двоичный файл в режиме чтения и записи;
  • «wb+» — открываем двоичный файл в режиме чтения и записи, существующий файл очищается;
  • «ab+» — двоичный файл открывается или создается для исправления существующий информации и добавления новой в конец файла.

Функция возвращает в файловой переменной f значение NULL в случае неудачного открытия файла. После открытия файла доступен 0-й его байт, указатель файла равен 0, значение которого по мере чтения или записи смещается на считанное (записанное) количество байт. Текущие значение указателя файла — номер байта, начиная с которого будет происходить операция чтения или записи.

Для закрытия файла предназначена функция fclose:

int fclose(FILE *filename);

Она возвращает 0 при успешном закрытие файла и EOF в противном случае.

Функция remove предназначена для удаления файлов:

int remove(const char *filename);

Эта функция удаляет с диска файл с именем filenema. Удаляемый файл должен быть закрыт. Функция возвращает ненулевое значение, если файл не удалось удалить.

Для переименования файлов предназначена функция rename:

int rename(const char *oldfilename, const char *newfilename);

Первый параметр — старое имя файла, второй — новое. Возвращает 0 при удачном завершении программы.

Чтение из двоичного файла осуществляется с помощью функции fread:

fread(void *ptr, size, n, FILE *filename);

Функция fread считывает из файла filename в массив ptr n элементов размера size. Функция возвращает количество считанных элементов. После чтения из файла его указатель смещается на n*size байт.

Запись в двоичный файл осуществляется с помощью функции fwrite:

fwrite(const void *ptr, size, n, FILE *filename);

Функция fwrite записывает в файл filename из массива ptr n элементов размера size. Функция возвращает количество записанных элементов. После записи информации в файл указатель смещается на n*size байт.

Для контроля достижения конца файла есть функция  feof:

int feof(FILE *filename);

Она возвращает ненулевое значение если достигнут конец файла.

Для более точного усвоения материала предлагаю рассмотреть пару стандартных задач.

Задача 1

Создать двоичный файл D:\\game\\noobs.dat и записать в него целое число n и n вещественных чисел.

Решение:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
setlocale (LC_ALL, "RUS");
int n, i;
double a;
FILE *f; //описываем файловую переменную
//создаем двоичный файл в режиме записи
f=fopen("D:\\game\\noobs.dat", "wb");
//ввод числа n
cout<<"n="; cin>>n;
fwrite(&n, sizeof(int), 1, f);
//цикл для ввода n вещественных чисел
for (i=0; i<n; i++)
{
//ввод очередного вещественного числа
cout<<"a=";
cin>>a;
//запись вешественного числа в двоичный файл
fwrite(&a, sizeof(double), 1, f);
}
//закрываем файл
fclose(f);
system("pause");
return 0;
}

 

Задача 2

Вывести на экран содержимого созданного в прошлой задаче двоичного файла D:\\game\\noobs.dat

Решение:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
setlocale (LC_ALL, "RUS");
int n, i;
double *a;
FILE *f; //описываем файловую переменную
//открываем существующий двоичный файл в режиме чтения
f=fopen("D:\\game\\noobs.dat", "rb");
//считываем из файла одно целое число в переменную n
fread(&n, sizeof(int), 1, f);
//вывод n на экран
cout<<"n="<<n<<endl;
//выделение памяти для массива из n чисел
a=new double[n];
//чтение n вещественных чисел из файла в массив a
fread(a, sizeof(double), n, f);
//вывод массива на экран
for (i=0; i<n; i++)
cout<<a[i]<<"\t";
cout<<endl;
//закрываем файл
fclose(f);
system("pause");
return 0;
}

 

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

 

1
2
3
4
5
6
7
8
9
10
int n, i;
double a;
FILE *f;
f=fopen("D:\\game\\noobs.dat", "rb");
for (i=0; i<15; i++)
fread(&a, sizeof(double), 1, f);
fclose(f);
f=fopen("D:\\game\\noobs.dat", "rb");
fread(&a, sizeof(double), 1, f);
fclose(f);

 

Как видно, такое чтение чисел из файла, а затем повторное открытие файла — не самый удобный способ. Гораздо удобнее будет использовать функцию fseek перемещения указателя файла к заданному байту.

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

Функция устанавливает указатель текущий позиции файла F в соответствии со значением начала отсчета origin и смещения offset. Параметр offset равен количеству байтов, на которые будет смещен указатель файла относительно начала отсчета, заданного параметром origin. В качестве значения для параметра origin должно быть взято одно из следующих значений отсчета смещения offset, определенных в заголовке stdio.h:

  • SEEK_SET — с начала файла;
  • SEEK_CUR — с текущей позиции;
  • SEEK_END — с конца файла.

Функция возвращает нулевое значение при успешном выполнение операции, ненулевое — при возникновении сбоя при выполнении смещения

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

Задача 3

В созданном раннее двоичном файле D:\\game\\noobs.dat, поменять местами наибольшее и наименьшее из вещественных чисел.

Алгоритм решения задачи состоит из следующих этапов:

  1. чтение вещественных из файла в массив a.
  2. поиск в массиве а максимального (max) и минимального (min) значения и их номеров (imax, imin).
  3. перемещения указателя файла к максимальному значению и запись min.
  4. перемещения указателя файла к минимальному значению и запись max.

Ниже приведен текст программы решения задачи с комментариями.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
setlocale (LC_ALL, "RUS");
int n, i, imax, imin;
double *a, max, min;
FILE *f;
//открытие файла в режиме чтения и записи
f=fopen("D:\\game\\noobs.dat", "rb+");
//считываем из файла в переменную n количество
//вещественных чисел в файле
fread(&n, sizeof(int), 1, f);
cout<<"n="<<n<<endl;
//выделяем память для хранения вещественных чисел,
//которые будут храниться в массиве a
a=new double[n];
//считываем из файла в массив а вещественные числа
fread(a, sizeof(double), n, f);
//поиск максимального и минимального элементов
//в массиве а и их индексов
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);
//освобождение памяти
delete []a;
system("pause");
return 0;
}

 

Итак, мы рассмотрели основные принципы работы с файлами в C++. В следующих уроках они вам еще встретятся, поэтому усвойте их как можно лучше.

4 комментария

Перейти полю для комментария

  1. rrrFer

    непонятно, почему при обработке текстовых файлов ты использовал потоки, а при обработке двоичных — вот этот FILE…

    >>Итак, мы рассмотрели основные принципы работы с файлами в C++
    вобщем-то «в Си»

    1. А. С. Третьяков

      О том, что программы написаны именно на C++ говорят хотя бы операции new и delete, двойная косая черта (//) и ссылки.
      При работе с двоичными файлами использовалась переменная-указатель, почему бы и нет?

  2. Eli0t

    «Возвращает 0 при успешном закрытие файла и NULL в противном случае. Функция remove предназначена для удаления файлов.»
    NULL это задефайненный 0 (#define NULL 0).

    1. А. С. Третьяков

      Спасибо что сообщили. Ошибка исправлена.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Вы можете использовать эти теги HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Проверка на человечность *