странная проблема сериализации объекта при разборе файла - PullRequest
0 голосов
/ 30 июня 2010

У меня странная проблема с сериализацией объектов. в документации к файлу указано следующее:

Ввод начинается с 4-байтового тега. который идентифицирует сегмент TDMS ( "TDSm"). Следующие четыре байта используются в качестве битовой маски, чтобы указать какие данные сегмента содержит. Эта битовая маска называется как ToC (Оглавление). любой комбинация следующих флагов может быть закодирован в ToC: следующие четыре байты содержат номер версии (32-битный целое число без знака), которое указывает самая старая TDMS ревизия сегмента в соответствии. Во время этого написание, номер версии 4713. Единственная предыдущая версия TDMS имеет номер 4712. Следующие восемь байтов (64-разрядное целое число без знака) длина оставшегося сегмента (общая длина сегмента минус длина поводка в). Если дальше сегменты добавляются в файл, этот номер может быть использован для поиска отправная точка следующего сегмент. Если приложение встретилось серьезная проблема при записи в Файл TDMS (сбой, отключение электроэнергии), все байты этого целого числа могут быть 0xFF. Это может произойти только до последнего сегмент в файле. Последние восемь байты (64-разрядное целое число без знака) опишите общую длину метаинформация в сегменте. это информация используется для произвольного доступа к необработанным данным. Если сегмент не содержит метаданных вообще (свойства, индексная информация, объект список), это значение будет 0.

так что я реализован как

class TDMsLEADIN {
public:
    char   Signature[4];    //TDSm
    __int32     Toc;
    unsigned __int32     vernum;
    unsigned __int64  nextSegmentOff;
    unsigned __int64  rawDataOff;
};
fread(&leadin,sizeof(TDMsLEADIN),1,f);

тогда я получил подпись = "TDsm", TOc = 6, vernum = 4712, как и ожидалось. nextSegmentOff = 833223655424, rawDataOff = 8589934592, но ожидается как nextSegmentOff, так и rawDataOff = 194

затем я делю класс на две части и читаю две две части отдельно

class TDMsLEADIN {
public:
    char   Signature[4];    //TDSm
    __int32     Toc;
    unsigned __int32     vernum;

};
class TDMsLeadINend{
public:
    unsigned __int64 nextSegmentOff;
    unsigned __int64 rawDataOff;
};
    fread(&leadin,sizeof(TDMsLEADIN),1,f);
    fread(&leadin2,sizeof(TDMsLeadINend),1,f);

тогда я получил nextSegmentOff, rawDataOff, как и ожидалось = 194. мой вопрос, что не так с оригинальным кодом? почему это работает, когда я разбить его на две части? я попытался unsigned long long вместо unsigned __int64, но все же результат тот же. это довольно странно.

Спасибо

Ответы [ 2 ]

1 голос
/ 30 июня 2010

Вы, похоже, просто читаете и пишете двоичные данные в структуре напрямую.

Как правило, компилятор выравнивает структурные данные для производительности, поэтому, когда это одна структура, существует скрытая 32-битная панель между vernum и nextSegmentOff для выравнивания nextSegmentOff. Когда он разделен на две структуры, такого дополнительного заполнения нет, и вы читаете четыре байта заполнения и четыре байта реальных данных в nextSegmentOff.

Вы можете проверить это, сравнив sizeof(TDMsLEADIN [second version]) + sizeof(TDMsLeadINend) с sizeof(TDMsLEADIN [first version])

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

0 голосов
/ 30 июня 2010

Ваша проблема в том, что ваш компилятор не упаковал структуру, поэтому все члены находятся рядом друг с другом.Например, ваш компилятор, возможно, решил, что ему нравится, чтобы 64-битные переменные были выровнены в памяти по 64-битной схеме, и вставил 4-байтовое заполнение в вашу структуру, чтобы сделать это.

Если вы действительно нужна производительность ввода-вывода, которую это обеспечивает, вы обычно можете сказать компилятору упаковать структуру, но 1) производительность может пострадать, когда вы используете невыровненные элементы в структуре, и 2) ваш код будет непереносимым,поскольку разные компиляторы определяют это по-разному.См. Visual C ++ эквивалент __attribute__ GCC ((__packed __)) для краткого описания способов сделать это на разных компиляторах.

Переносимый, но несколько более прозаичный способ:

fread(&lead.Signature, 4, 1, f);
fread(&lead.Toc, sizeof(__int32), 1, f);
...
...