2016-06-11 3 views
1

У меня есть переменная типа uint8_t, которую я хотел бы сериализовать и записать в файл (который должен быть довольно портативным, по крайней мере для Windows, на что я нацелен) ,Преобразование uint8_t в его двоичное представление

Попытка записать его в файл в своей бинарной форме, я наткнулся на эту рабочий сниппета:

uint8_t m_num = 3; 
unsigned int s = (unsigned int)(m_num & 0xFF); 
file.write((wchar_t*)&s, 1); // file = std::wofstream 

Во-первых, позвольте мне убедиться, что я понимаю, что делает этот фрагмент кода - это берет мой вар (который в основном является unsigned char, длиной 1 байт), преобразует его в unsigned int (который имеет длину 4 байта и не очень переносимый) и использует & 0xFF«извлекает» только младший байт.

Теперь, есть две вещи, которые я не понимаю:

  1. Почему превратить его в unsigned int в первую очередь, почему я не могу просто сделать что-то вроде
    file.write((wchar_t*)&m_num, 1); или reinterpret_cast<wchar_t *>(&m_num)? (Ref)
  2. Как бы сериализовать более длинный тип, скажем, uint64_t (длиной 8 байтов)? unsigned int здесь может быть или не быть.
+2

Этот код является ужасным, не переносным и имеет неопределенное поведение. Он также нацелен на поток широких символов, которых вы, вероятно, не знаете. –

+0

Привет @AlanStokes, спасибо за ваш комментарий. Не могли бы вы объяснить, почему этот код плохой и не переносимый? Что было бы более мудрым способом сделать это? – Asaf

ответ

1

uint8_t является 1 байт, так же как char

wchar_t 2 байта в ОС Windows, 4 байта в Linux. Это также зависит от сущности. Вы должны избегать wchar_t, если переносимость вызывает беспокойство.

Вы можете использовать только std::ofstream. Windows имеет дополнительную версию для std::ofstream, которая принимает имя файла UTF16. Таким образом, ваш код совместим с именами файлов Windows UTF16, и вы все равно можете использовать std::fstream. Например,

int i = 123; 
std::ofstream file(L"filename_in_unicode.bin", std::ios::binary); 
file.write((char*)&i, sizeof(i)); //sizeof(int) is 4 
file.close(); 
... 
std::ifstream fin(L"filename_in_unicode.bin", std::ios::binary); 
fin.read((char*)&i, 4); // output: i = 123 

Это относительно просто, потому что оно хранит целые числа. Это будет работать на разных системах Windows, потому что Windows всегда малозначительна, а размер int всегда равен 4.

Но некоторые системы имеют большой размер, вам придется иметь дело с этим отдельно.

Если вы используете стандартный ввод-вывод, например fout << 123456, тогда целое число будет сохранено в виде текста «123456». Стандартный ввод-вывод совместим, но он занимает немного больше места на диске и может быть немного медленнее.

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

+0

Привет @BarmakShemirani, спасибо за ваш ответ! Теперь это имеет большой смысл! Я думаю, что char гораздо более портативный. Не могли бы вы объяснить, почему лучше использовать стандартный ввод-вывод? и какие другие проблемы с переносимостью присутствуют? – Asaf

+0

Я добавил больше объяснений в ответ. Существует также проблема с текстом. Если вы хотите, чтобы совместимость с другими системами, обычной практикой является преобразование UTF16 в UTF8. Я не знаю, включаете ли вы текст в свой файл, я не понял этого. –

0

Невозможно записать значения unit8_t в wofstream, поскольку wofstream записывает только широкие символы и не обрабатывает двоичные значения вообще.

Если вы хотите написать большой символ, представляющий кодовую точку от 0 до 255, тогда ваш код будет правильным.

Если вы хотите записать двоичные данные в файл, то ближайший эквивалент - ofstream, что позволит вам писать байты.

Чтобы ответить на ваши вопросы:

  1. wofstream::write пишет широкие символы, а не байты. Если вы переинтерпретируете адрес m_num в качестве адреса широкого символа, вы будете писать 16-битный или 32-разрядный (в зависимости от платформы) широкий характер, из которых первый байт (то есть наименее значимый или самый значительный, в зависимости от платформы) - это значение m_num, а оставшиеся байты происходят в памяти после m_num. В зависимости от кодировки символов широких символов это может быть даже не допустимым символом. Даже если это действительно так, это во многом абсурд. (Существуют и другие возможные проблемы, если wofstream::write ожидает выравнивания по ширине, а не по байтам, или если m_num сразу же следует нечитаемой памятью).

  2. Если вы используете wofstream, то это беспорядок, и я не буду обращаться к нему. Если вы переключитесь на байт-ориентированный ofstream, у вас есть два варианта. 1. Если вы будете только читать файл в той же системе, то file.write(&myint64value,sizeof(myint64value)) будет работать. Последовательность, в которой записаны байты 64-битного значения, будет неопределенной, но такая же последовательность будет использоваться при чтении назад, поэтому это не имеет значения. Не делайте попробуйте сделать что-то аналогичное с wofstream, потому что это опасно! 2. Извлеките каждый из 8 байтов myint64value отдельно (сдвиньте вправо на кратное 8 бит, а затем возьмите нижние 8 бит), а затем запишите его. Это полностью переносимо, потому что вы контролируете порядок записи байтов.

+0

Спасибо @nugae! Что касается пункта № 2, проблема в том, что это так, верно? используя функцию, подобную 'htons',' htonl' и друзьям (в основном устанавливая стандарт на big-endian), решит проблему, не так ли? – Asaf

+0

Да, это утверждение. Если вы остаетесь в рамках одной системы, это не имеет значения, но если вам нужна совместимость между системами, то это произойдет. 'htonl' и его родственники будут работать, но (согласно документации) они доходят только до' uint32_t'. Поэтому, если вы хотите сделать 'uint64_t', вам нужно будет сделать нижнюю половину (' & 0xffffffffU') и верхнюю половину ('>> 32') отдельно. Вы можете упаковать их в свою собственную функцию «htonl64» или (лучше) в свои собственные функции write64 и 'read64'. – nugae