Re: Запись и чтение структур в/из файл(а)
От: AndrewS42 Россия  
Дата: 06.06.05 06:58
Оценка:
Здравствуйте, IT, Вы писали:

IT>Всё зависит от многих вещей, как всегда


Это точно! Несмотря на то, что топику уже несколько лет, я хотел бы вставить свои 5 копеек в обсуждаемую тему, тем более, что эта тема находится в статьях сайта, а значит не имеет срока давности.
Наиболее правильным и переносимым (но медленным) будет вообще не сохранять в файл структуру целиком. Более того, не желательно даже int записывать в файл напрямую. Одна из причин уже была озвучена в топике: несовпадающие размеры int в разных компиляторах даже на одной платформе. Другая причина кроется в переносимости. Не факт, что ваша программа (или даже её отдельный модуль) никогда не переедут под UNIX. В отличие от Windows, UNIX существует на большом зоопарке платформ. Некоторые из них имеют big-endian представления чисел (первым идёт старший байт). Если на такой платформе сохранить int в файл как есть, а затем прочитать этот файл на Intel-платформе, то мы получим совершенно другое число. Например (пусть размер int будет 32 бита), если на big-endian сохранили 0x12345678, то в little-endian прочитаем 0x78563412.
Для большинства эти детали не существенны, так как не так уж и много программистов (надо признаться, и я в том числе) пишут сразу для нескольких платформ. Но, по крайней мере, задуматься о потенциальных проблемах переносимости надо.
Как же сохранять данные в файл, чтобы и файл, и программа его читающая были переносимы? Ответ уже был в этом топике: сохранять всё в xml или другом текстовом файле. Однако, если объём данных велик, и от двоичных данных никуда не деться, то сохранять все данные следует побайтно. Пример сохранения 32-битного int (предполагаем, что система, где char имеет размер, не равный 8 бит, нам не попадётся):

#include <stdio.h>

const char filename[] = "file.dat";

void read_int(FILE* f, int* d)
{
  unsigned char buf[4];
  unsigned char* p = buf;
  
  fread(buf, sizeof(buf), 1, f); // portable
  *d = *p++;
  *d += *p++ << 8;
  *d += *p++ << 16;
  *d += *p++ << 24;
//  fread(d, sizeof(*d), 1, f); // not portable
} // read_int

void write_int(FILE* f, int d)
{
  unsigned char buf[4];
  unsigned char* p = buf;
  
  *p++ = d & 0xff;
  *p++ = (d >> 8) & 0xff;
  *p++ = (d >> 16) & 0xff;
  *p = (d >> 24) & 0xff;
  fwrite(buf, sizeof(buf), 1, f); // portable
//  fwrite(&d, sizeof(d), 1, f); // not portable
} // write_in

void help() 
{
  fprintf(stderr, "Usage: fileio {w|r}\n");
  exit(1);
} // help()

int main(int argc, char *argv[])
{
  const int data = 0x12345678;
  FILE* fi;
  FILE* fo;
  int tmp;
  char err_msg[255];
  
  if (argc != 2) help();
  if (! strcmp(argv[1], "w")) {
    fo = fopen(filename, "w");
    if (! fo) {
      sprintf(err_msg, "Can not open file '%s'", filename);
      perror(err_msg);
      exit(1);
    }
    write_int(fo, data);
    fclose(fo);
  }
  else if (! strcmp(argv[1], "r")) {
    fo = fopen(filename, "r");
    if (! fo) {
      sprintf(err_msg, "Can not open file '%s'", filename);
      perror(err_msg);
      exit(1);
    }
    read_int(fo, &tmp);
    fclose(fo);
    printf("Readed %#08x. Must be %#08x\n", tmp, data);
  }
  else 
    help();
  return 0;
} // main()



Если гложут вопросы быстродействия, особенно в свете того, что big-endian систем не много и ради этой редкости мы замедляем считывание файлов, то необходимо ввести условную компиляцию: на little-endian системе читать int целиком, на big-endian – побайтно. Опять же, нельзя забывать про отличия размера int на разных платформах.
Хочу отметить, что я далеко не гуру в этом вопросе, и все мои замечания основаны лишь на изучении и портировании Open source ПО на компьютер RM200 (RISC-процессор R4000 (big-endian), ОС SINIX).
Всего Вам
Андрей
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.