Ответ
Двоичная (бинарная) сериализация/десериализация подразумевает прямую запись и чтение бинарного представления объектов в память или файл.
Плюсы:
- Высокая скорость: Отсутствие затрат на парсинг текстовых форматов (JSON, XML). Данные копируются или читаются напрямую.
- Компактность: Бинарное представление обычно занимает значительно меньше места, чем текстовое (нет имён полей, скобок, кавычек).
- Сохранение точности: Прямая запись бинарных данных типов с плавающей точкой (
float,double) без потери точности из-за преобразования в строку и обратно. - Поддержка сложных структур: Можно сериализовать упакованные структуры (POD-типы), битовые поля, массивы фиксированного размера.
Минусы:
- Отсутствие переносимости (Portability):
- Endianness (порядок байт): Данные, записанные на little-endian системе (x86), будут некорректно прочитаны на big-endian системе (некоторые ARM, PowerPC) без преобразования.
- Размер и выравнивание типов:
intможет быть 16, 32 или 64 бита в зависимости от платформы и компилятора. Выравнивание полей в структуре (padding) может различаться. - Структура классов: Не-POD типы (с виртуальными функциями, указателями) обычно нельзя сериализовать простым копированием памяти.
- Хрупкость формата: Любое изменение в структуре данных (порядок, тип или размер полей) ломает обратную совместимость. Требуется версионирование и миграция данных.
- Отсутствие человекочитаемости: Данные нельзя просмотреть или отредактировать в текстовом редакторе, сложнее отлаживать.
- Проблемы безопасности: Прямая загрузка данных в память может быть уязвима для атак, если данные повреждены или сформированы злоумышленником.
Пример сериализации POD-структуры с учётом выравнивания:
#include <cstdint>
#include <fstream>
#include <cstring>
// #pragma pack отключает выравнивание для переносимости.
#pragma pack(push, 1) // Гарантируем, что структура упакована без паддинга.
struct SerializableData {
std::int32_t id; // Используем типы фиксированного размера.
double value;
char tag[16]; // Массив фиксированного размера вместо std::string.
};
#pragma pack(pop)
bool writeData(const SerializableData& data, const std::string& filename) {
std::ofstream file(filename, std::ios::binary);
if (!file) return false;
file.write(reinterpret_cast<const char*>(&data), sizeof(data));
return file.good();
}
bool readData(SerializableData& data, const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) return false;
file.read(reinterpret_cast<char*>(&data), sizeof(data));
return file.good();
}
// Важно: Этот код всё ещё не переносим между системами с разным endianness!
// Для переносимости нужно преобразовывать многобайтовые типы (id, value) в сетевой порядок байт (htonl/ntohl и т.д.).