Запись в файл. fwrite() fread();

Рейтинг: 3Ответов: 2Опубликовано: 23.12.2014

Доброго времени, уважаемые.

Пример взял с сайта для изучения C++. При записи в файл в нем самом отображается ерунда, но не то, что надо. С чего бы это?

   #include <stdio.h>
#include <stdlib.h>

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;
}

Ответы

▲ 9Принят

fread/fwrite функции читают/пишут данные в бинарном формате. Если хочется человеко-читаемый (текстовый) формат, то можно использовать fscanf/fprintf вместо fread/fwrite соответственно.

Приведённый в вопросе код должен работать как есть.

Что конкретно fwrite запишет на диск, может зависеть от платформы (размер типа (LLP64, LP64 модели), порядок байтов, и даже побитовое представление типов (дополнительный код, обратный код), выравнивание типов).

Например, если файл скопировать на другую платформу, то не обязательно что fread сможет его прочитать корректно.

На моей машине test файл содержит:

$ xxd test
0000000: f628 5c8f c275 2840 6500 0000 8fe0 0100  .(\..u(@e.......
0000010: 0000 0000                                ....

Первая колонка, нумерует байты, в каждой строке по 16 байт, представленных командой xxd ввиде шестнадцатеричных чисел (каждые две цифры соответствуют одному байту). Самая правая колонка показывает печатаемые байты (точка используется для непечатаемых в ascii символов).

Вот байты соответствующие каждому значению:

  d  f628 5c8f c275 2840
  i  6500 0000
  l  8fe0 0100 0000 0000

Можно сразу сказать, что int является 32-битным типом (4 байта, предполагая 1 байт = 8 бит), а long 64-битным типом (что верно для lp64 модели) с порядком байтов от младшего с старшему (little-endian) на моей машине.

Если записать байты для i и l в порядке от старшего к младшему (big-endian) как обычно числа записаны на бумаге пишут:

  • int i 00000065 в шестнадцатеричной системе соответствует 101 в десятичной системе(6516 = 10110), что является корректным значением для i
  • long l 000000000001e08f в шестнадцатеричной системе соответствует 123023 в десятичной системе (1e08f16 = 12302310), что также верно.

Число d записано как число двойной точности в IEEE 754 формате --
d = ±знак · (1 + мантисса / 252) · 2порядок − 1023:

d  402875c28f5c28f6

(cнова переписал байты от старшего к младшему) Или в двоичной системе -- все 64 бита числа d:

d 0100000000101000011101011100001010001111010111000010100011110110

Самый левый бит это знак, в данном случае он 0, то есть d является положительным, затем 11 бит порядок = 100000000102 = 102610, оставшиеся 52 бита мантисса:

1000011101011100001010001111010111000010100011110112
= 238127830297215010
= 875c28f5c28f616

Всё вместе:

d = (1 + 2381278302972150 / 252) · 21026 − 1023
= 3442438965171323 / 281474976710656
= 12.23

12.23 также верным значением является.

▲ 1

в файле должно быть (при 64 битах, little-endian) : f628 5c8f c275 2840 6500 0000 8fe0 0100 0000 0000

или, если смотреть в символьном виде, что то типа - ö(.Âu(@e^@^@^@.à^A^@^@^@^@^@ (сильно зависит от того, чем вы смотрите)

А что вы хотели увидеть?

Ну и на вский случай, никогда так не делайте, не пишите sizeof(T) в бинарный файл, размер и порядок следования байт pod типа зависит от разрядности и архитектуры, под которую вы собираете.