C# Сериализациядесериализация классов в бинарный файл
Хочу сериализовать класс (содержит несколько структур). Первоначально писал в текстовый файл: 1 строка = 1 экземпляр класса. Столкнулся с проблемой - необходимо считывать последнюю строку, чтобы узнавать последний ID. Но произвольный доступ отсутствует, что вынуждает загружать весь файл. Данных предполагается много, так что этот вариант не подходит. Посему пришел к необходимости писать в бинарный файл. и вот тут подходим к самой сути вопроса:
Как правильно осуществлять хранение (запись) данных в бинарном файле?
Пока что основная идея записывать по-членно класс в файл через BinaryWriter. Читать таким же образом восстанавливая состояние экземпляра.
Код:
namespase n1
{
struct structHealth
{
byte liver;
byte eye;
byte brain;
}
struct structCloth
{
uint head;
uint chest;
uint legs;
}
class Human
{
ulong ID;
string Name;
structHealth Health;
structCloth Cloth;
}
public Save()
{
using (BinaryWriter _br = new BinaryWriter(File.OpenWrite("lstHuman")))
{
_br.Write(this.ID);
_br.Write(Health.liver);
//...
_br.Write(Cloth.Head);
//...
_br.Write(this.Name);
}
}
public Load()
{
//аналогично только чтение.
}
}
{
struct structHealth
{
byte liver;
byte eye;
byte brain;
}
struct structCloth
{
uint head;
uint chest;
uint legs;
}
class Human
{
ulong ID;
string Name;
structHealth Health;
structCloth Cloth;
}
public Save()
{
using (BinaryWriter _br = new BinaryWriter(File.OpenWrite("lstHuman")))
{
_br.Write(this.ID);
_br.Write(Health.liver);
//...
_br.Write(Cloth.Head);
//...
_br.Write(this.Name);
}
}
public Load()
{
//аналогично только чтение.
}
}
строковый член Name, как водится будет переменной длинны. Как в таком случае организовать произвольный доступ? Ведь блоки данных будут разной длинны.
2. Строковые значения нужно конвертировать в массивы символов строго заданного размера. Размер нужно выбрать такой, чтобы все возможные значения влезали, даже с учётом будущего.
Для удобства определяем константу с размером и создаём дополнительный метод:
Код:
const int NAME_SIZE = 20;
char[] getFixedSizeArray(string name)
{
if (name.Length > NAME_SIZE)
throw new ArgumentOutOfRangeException("name");
char[] result = new char[NAME_SIZE];
name.CopyTo(0, result, 0, name.Length);
return result;
}
char[] getFixedSizeArray(string name)
{
if (name.Length > NAME_SIZE)
throw new ArgumentOutOfRangeException("name");
char[] result = new char[NAME_SIZE];
name.CopyTo(0, result, 0, name.Length);
return result;
}
Код:
using (FileStream fs = File.OpenWrite("test.bin"))
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(id);
writer.Write(getFixedSizeArray(name));
}
using (FileStream fs = File.OpenRead("test.bin"))
using (BinaryReader reader = new BinaryReader(fs))
{
id = reader.ReadInt32();
name = new string(reader.ReadChars(NAME_SIZE)).TrimEnd('\0');
}
using (BinaryWriter writer = new BinaryWriter(fs))
{
writer.Write(id);
writer.Write(getFixedSizeArray(name));
}
using (FileStream fs = File.OpenRead("test.bin"))
using (BinaryReader reader = new BinaryReader(fs))
{
id = reader.ReadInt32();
name = new string(reader.ReadChars(NAME_SIZE)).TrimEnd('\0');
}
Таким образом, все записи будет одинаковой длины и можно перемещаться к любой по индексу и свободно её перезаписывать.
Однако, можно словить нехилые грабли, если в строках будут юникодные суррогатные пары. Какую кодировку выбрать (в Binary райтере и ридере), чтобы избежать проблем - предлагаю поломать голову самостоятельно.
Каюсь, не уточнил область применения, и где вообще это всё.
В качестве хобби пишу логику игры. Предполагается значительный массив данных на каждого юнита, не говоря уже о ГГ. В серьёз подумываю о хранении сего дела в SQL базе. Единственное что не знаю как это скажется на быстродействии. Пока что все на этапе разработки и намётки. Сейчас намерен написать генератор персонажей, хотелось бы куда то сохранять, поэтому и возникли такие вопросы.