C / C ++: чтение и запись данных меток времени в файл с поддержкой нескольких архитектур - PullRequest
3 голосов
/ 07 декабря 2011

Из /usr/include/time.h:

/* Used by other time functions. */
struct tm
{
int tm_sec;. . . /* Seconds..[0-60] (1 leap second) */
int tm_min;. . . /* Minutes..[0-59] */
int tm_hour;. . . /* Hours.. [0-23] */
int tm_mday;. . . /* Day... [1-31] */
int tm_mon;. . . /* Month.. [0-11] */
int tm_year;. . . /* Year.- 1900. */
int tm_wday;. . . /* Day of week..[0-6] */
int tm_yday;. . . /* Days in year.[0-365].*/
int tm_isdst;.. . /* DST... [-1/0/1]*/

#ifdef. __USE_BSD
long int tm_gmtoff;. . /* Seconds east of UTC. */
__const char* tm_zone;. / Timezone abbreviation. */
#else
long int __tm_gmtoff;.. /* Seconds east of UTC. */
__const char* __tm_zone;. / Timezone abbreviation. */
#endif
};

Если вы хотите записать эту структуру в файл и хотите, чтобы ваша программа считывала ее обратно и имела поддержку нескольких арок (то есть, 32-битная версия записывает ее, 64-битная версия читает), вам придется сделайте несколько хаков, чтобы убедиться, что это одинаковый размер для каждой системы. Кто-нибудь знает лучший способ сохранить метки времени, которые не зависят от архитектуры? Например, я хочу иметь возможность записать некоторую структуру, например, time_t или struct tm, в файл и прочитать ее обратно для любой архитектуры. У кого-нибудь есть опыт или совет по этому поводу? Является ли struct tm лучшим способом сохранить метку времени в C / C ++? Я понимаю, что использование этой структуры требует значительных затрат.

В настоящее время я переопределил структуру следующим образом:

//Force 32 bit format and space requirements
struct tm32
{
int tm_sec;. . . /* Seconds..[0-60] (1 leap second) */
int tm_min;. . . /* Minutes..[0-59] */
int tm_hour;. . . /* Hours.. [0-23] */
int tm_mday;. . . /* Day... [1-31] */
int tm_mon;. . . /* Month.. [0-11] */
int tm_year;. . . /* Year.- 1900. */
int tm_wday;. . . /* Day of week..[0-6] */
int tm_yday;. . . /* Days in year.[0-365].*/
int tm_isdst;.. . /* DST... [-1/0/1]*/

int tm_gmtoff; // this does nothing but hold space
int tm_zone; // this does nothing but hold space
};

Поскольку функции (mktime (), time (), gmtime (), strftime ()), которые я использую для манипулирования структурой tm, похоже, даже не смотрят на последние два поля в структуре struct tm, Я просто использую целые числа в качестве заполнителей в своих позициях, чтобы размер всегда был одинаковым. Тем не менее, я все еще ищу лучшее решение для этого ...

РЕДАКТИРОВАТЬ: типы int, которые я использовал в вышеупомянутом исправлении, могли быть int32_t или любым другим выбранным типом фиксированной ширины.

Ответы [ 4 ]

5 голосов
/ 07 декабря 2011

Обеспечение того, чтобы сохраненные данные записывались таким образом, чтобы их можно было прочитать на нужных платформах, на мой взгляд, не квалифицируется как «взлом». Однако слепое сохранение (возможно, упакованной) структуры в двоичном формате и ожидание простого и удобного чтения ее обратно.

Вам необходимо обрабатывать каждое поле отдельно, так как компилятор может добавить заполнение между полями, которое появится в «двоичном дампе» struct, но это полностью зависит от компилятора и, следовательно, очень плохо для взаимодействия между платформами. .

Я бы, вероятно, выбрал разумную точность для собственных int полей структуры, скажем, 32 бита, и написал бы что-то вроде этого:

void tm_serialize(FILE *out, const struct tm *tm)
{
  int32_serialize(out, tm->tm_sec);
  int32_serialize(out, tm->tm_min);
  /* and so on */
}

struct tm tm_deserialize(FILE *in)
{
  struct tm tm;
  tm.tm_sec = int32_deserialize(in);
  tm.tm_min = int32_deserialize(in);
  /* and so on */
  return tm;
}

Где, конечно, int32_serialize() и int32_deserialize() просто пишут (и читают) двоичное представление 32-разрядного целого числа в известном (например, с прямым порядком байтов) формате, которое хорошо определено и легко читается назад на любую платформу.

ОБНОВЛЕНИЕ: Сериализация строк, конечно, может быть выполнена точно таким же образом, один популярный формат такой же, как макет C в памяти, т. Е. Массив char с нулевым символом в конце. Тогда у вас будет просто:

void string_serialize(FILE *out, const char* string);
int  string_deserialize(FILE *in, char *string, size_t max);

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

В приведенном выше примере я не стремился к минимизации пространства, сводимой к минимуму, поскольку комментатор указал, что многие из полей, которые int во время выполнения, на самом деле не нуждаются в такой точности, поэтому они могут быть сериализовано во что-то меньшее. Если вы хотите сделать это, конечно, было бы тривиально изобрести соответствующие функции, такие как int8_serialize() и т. Д., И использовать правильную для каждого поля.

1 голос
/ 07 декабря 2011

struct tm обычно используется с функциями времени C / POSIX. Предполагая, что вы не используете его вне диапазонов, разрешенных этими функциями, есть несколько очевидных способов:

  • используйте strftime для записи, strptime для чтения. Преимущество: очень легко понять выходные данные - они записаны в обычном формате времени (например, в формате ISO)
  • вызовите mktime, чтобы преобразовать его в time_t, затем запишите его как двоичный файл или ASCII. Другое направление: localtime или gmtime. Преимущество: маленький, обычно 4 или 8 байт для двоичного файла. Не намного больше для ASCII.
0 голосов
/ 07 декабря 2011

gettimeofday(2) вернет количество секунд и микросекунд с начала эпохи;поскольку время имеет тип time_t, оно может быть 32-разрядным (что ставит конец света на 2038 ) или может быть больше, поэтому было бы разумно разыграть time_tдо uint64_t.Сохраните 64-разрядное целое число в указанном порядке байтов, используя такой инструмент, как htobe64(3).

0 голосов
/ 07 декабря 2011

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

В противном случае включите <stdint.h> и используйте такие типы, как uint32_t в преобразованной структуре.

...