Как записать double в файл

Обновлено: 04.07.2024

Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.

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

Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.

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

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

Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread

Функция возвращает число удачно прочитанных элементов, которые помещаются по адресу ptr. Всего считывается count элементов по size байт. Давайте теперь считаем наше число обратно в переменную.

fseek

Одной из важных функций для работы с бинарными файлами является функция fseek

Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения

  • SEEK_SET - начало файла
  • SEEK_CUR - текущее положение файла
  • SEEK_END - конец файла. К сожалению, стандартом не определено, что такое конец файла, поэтому полагаться на эту функцию нельзя.

В случае удачной работы функция возвращает 0.

Дополним наш старый пример: запишем число, затем сдвинемся указатель на начало файла и прочитаем его.

Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.

В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция

используется для того, чтобы назначить переменной pos текущее положение. Функция

используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.

возвращает текущее положение индикатора относительно начала файла. Для бинарных файлов - это число байт, для текстовых не определено (если текстовый файл состоит из однобайтовых символов, то также число байт).

Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.

Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.

Примеры

1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.

2. Пишем слова в бинарный файл. Формат такой - сначало число букв, потом само слово без нулевого символа. Ели длина слова равна нулю, то больше слов нет. Сначала запрашиваем слова у пользователя, потом считываем обратно.

3. Задача - считать данные из текстового файла и записать их в бинарный. Для решения зачи создадим функцию обёртку. Она будет принимать имя файла, режим доступа, функцию, которую необходимо выполнить, если файл был удачно открыт и аргументы этой функции. Так как аргументов может быть много и они могут быть разного типа, то их можно передавать в качестве указателя на структуру. После выполнения функции файл закрывается. Таким образом, нет необходимости думать об освобождении ресурсов.

4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.

5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).

6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.

Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.

email

Всё ещё не понятно? – пиши вопросы на ящик

fp=fopen ("results.txt","w+"); // Open file to write results
if(fp==NULL) cout << "Error! Can't open file!\n";
else
cout << "\n\nWriting to file. ";
for(i=0;i<numofexp;i++)
sprintf(str,"%+5.5f ",y);
cout<<str;
while(str[j]!='\0')
j=0;
if((i%4)==3) fputc('\n',fp);
>
>

fclose(fp);
cout << "\nWriting complete!";

и, соответственно чтение из файла:

fp=fopen ("results.txt","r");
if(fp==NULL) cout << "Error! Can't open file!\n";
else
while(c=getc(fp)!= EOF)
>
fclose(fp);

Так вот выводится просто куча мордочек(символ мордочки), а не цифры и все отстальное.

using namespace std;

ofstream output(L"results.txt", ios_base :: out | ios_base::trunc);
if (!output.is_open()) return;
output.setf(ios_base::fixed);

for (int n = 0 ; n < 5 ; n++)
output << double_array[n] << endl;

Чтение из файла:

using namespace std;

void main(int argc, char *argv[])
ifstream input(L"results.txt", ios_base::in);
if (!input.is_open()) return;

while (!input.eof())
string value; input >> value;
if (!value.empty())
cout << value << endl;
>

PS. Модераторам - почему ios_base :: out не отображается.

потому что без пробела написал, а :о это символ смайлика. почему в тэгах код это происходит это вопрос к админу, не ко мне

Спасибо большое Damarus, но можно ли все это осуществить тем диапазоном функций, каким пользовался я, а то препод будет задавать лишние вопросы?
P.S. Я могу весь код лабы привести, если надо.

FILE* output = fopen("results.txt", "w+");
if (output == 0) return;

for (int n = 0 ; n < 5 ; n++)
fprintf(output, "%f\n", double_array[n]);

Чтение из файла:

void main(int argc, char* argv[])
FILE* input = fopen("results.txt", "r");
if (input == 0) return;

while (!feof(input))
char szBuffer[32] = < 0 >;
fscanf(input, "%s", &szBuffer);
printf("%s\n", szBuffer);
>

Спасибо большое Damarus! Все работает, буду сдавать. Но если тебе не сложно, то скажи, что в моем коде неправильно. Код записи в файл вообщем то правильный, только не рациональный. В коде чтения проблемма здесь:
Результат логического выражения 0 или 1, так почему мордочки? Originally posted by Damarus
Можно проще. Запись в файл:

using namespace std;

void main(int argc, char *argv[])
ofstream output(L"results.txt",[color=green] ios_base::out[/color] | ios_base::trunc);

PS. Модераторам - почему ios_base :: out не отображается.


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

Перед изучением данной темы рекомендуется ознакомиться со следующими темами:

Содержание

  • 1. Класс BinaryWriter . Назначение
  • 2. Взаимодействие класса BinaryWriter с потоками опорных хранилищ
  • 3. Конструкторы класса BinaryWriter . Создание экземпляра
  • 4. Основные методы класса BinaryWriter
    • 4.1. Метод Write() — много перегруженных реализаций
      • 4.1.1. Запись одиночных значений. Перегруженный метод Write() . Пример
      • 4.1.2. Запись байтовых массивов byte[] в поток. Пример
      • 4.1.3. Запись символьных массивов char[] в поток. Пример

      Поиск на других ресурсах:

      1. Класс BinaryWriter . Назначение

      Класс BinaryWriter предназначен для записи данных в двоичном (бинарном) формате. Запись данных может осуществляться в файлы, сеть, изолированное хранилище, память и т.п. Во время записи строк существует возможность указания нужной кодировки. По умолчанию установлена кодировка UTF-8.
      Класс реализован в пространстве имен System.IO . Для того, чтобы использовать возможности этого класса нужно добавить строку

      Класс BinaryWriter записывает данные, которые представлены:

      • примитивными типами: int , float , double и другими;
      • строками типа string в указанной кодировке.
      2. Взаимодействие класса BinaryWriter с потоками опорных хранилищ

      Класс BinaryWriter (а также BinaryReader ) относится к адаптерам потоков. Это означает следующее. Чтобы получить доступ к файлу, сети или памяти, нужно использовать промежуточный класс опорного хранилища ( FileStream , MemoryStream , NetworkStream и т.д.).
      На рисунке отображено взаимодействие класса BinaryWriter с файловым хранилищем, которому соответствует класс FileStream .

      Рисунок. Взаимодействие класса BinaryWriter с файлом через класс FileStream

      3. Конструкторы класса BinaryWriter . Создание экземпляра

      Класс BinaryWriter имеет несколько конструкторов, наиболее распространенными из которых следующие:

      • output – ссылка на абстрактный класс Stream , являющийся вершиной иерархии классов ввода-вывода;
      • encoding – система кодировки (Unicode, UTF32, UTF8 или другая). По умолчанию установлена система кодировки UTF8.

      Как видно из примера, при создании потоков используется синтаксис с использованием ключевого слова using() . Это освобождает от необходимости закрывать потоки методом Close() , поскольку при таком подходе очистка ресурсов происходит автоматически.

      4. Основные методы класса BinaryWriter

      Метод имеет 20 перегруженных реализаций, основные из которых приведены ниже.

      4.1.1. Запись одиночных значений. Перегруженный метод Write() . Пример

      Чтобы записать одиночные значения используются методы Write() , которые имеют следующую общую форму

      здесь value – значение одного из стандартных (примитивных) типовв, которое нужно записать в поток.

      Пример.

      Результат выполнения программы

      4.1.2. Запись байтовых массивов byte[] в поток. Пример

      Запись байт массивов типа byte[] полезен, когда в поток нужно записывать массивы стандартных типов (например, массивы int[] , double[] , decimal[] и т.д.). Для конвертирования из стандартных типов в тип byte[] удобно использовать возможности класса BitConverter .

      • buffer – участок памяти, из которого будут записываться данные в поток побайтно;
      • index – позиция (индекс) начала в массиве buffer , с которой будет происходить запись в поток;
      • count – количество байт, которые нужно записать в поток.

      Результат выполнения программы

      4.1.3. Запись символьных массивов char[] в поток. Пример

      Для записи строк символов в виде массива char[] используются две следующие реализации метода Write() :

      Пример. В примере записывается строка типа string в файловый поток в виде символьного массива char[] .

      Результат выполнения программы

      4.2. Метод Seek() . Пример

      Метод Seek() устанавливает позицию в текущем потоке. Общая форма метода следующая:

      Значение origin есть перечислением и имеет тип

      Значения из перечисления могут быть следующими:

      Пример. В примере продемонстрированы следующие операции:

      • запись чисел в файл;
      • перезапись ранее записанных чисел;
      • дописывание числа в конец файла.

      Результат выполнения программы

      4.3. Метод Flush() . Пример

      Метод Flush() используется для очистки всех буферов текущего модуля записи. После этого можно записывать любые буферизированные данные на устройство, в которое на данный момент разрешено производить запись.

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

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

      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

      Задача 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

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

      Как видно, такое чтение чисел из файла, а затем повторное открытие файла — не самый удобный способ. Гораздо удобнее будет использовать функцию 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

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

      Читайте также: