Ar esate kada bandę išsaugoti objektų aibę į failą ir vėliau juos nuskaityti? Egzistuoja keli būdai kaip tai padaryti, bet šiandien norėčiau parodyti mano manymu vieną patogiausių, naudojant Protocol Buffers duomenų formatą. Efektyvus darbas su failais dažnai susiveda į srautų (angl. Stream) panaudojimą, todėl mums reikalingas būdas paversti objektą į binarinį formatą ir rezultatą išsaugoti į srautą. Tam aprašysime interfeisą IStreamSerializer:

public interface IStreamSerializer
{
    T Deserialize<T>(Stream stream) where T : class;
    void Serialize<T>(T obj, Stream stream) where T : class;
}

Darbui su Protocol Buffers iš .NET rekomenduoju Marc Gravell biblioteką protobuf-net:

PM> Install-Package protobuf-net

ProtocolBuffersStreamSerializer realizacija galėtų atrodyti taip:

public class ProtocolBuffersStreamSerializer : IStreamSerializer
{
    public T Deserialize<T>(Stream stream) where T : class
    {
        if (stream == null || stream.Length == 0) return null;

        return Serializer.DeserializeWithLengthPrefix<T>(stream, PrefixStyle.Base128);
    }

    public void Serialize<T>(T obj, Stream stream) where T : class
    {
        if (obj == null || stream == null) return;

        Serializer.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128);
    }
}

Atkreipkite dėmesį į metodus (Deserialize|Serialize)WithLengthPrefix. Tam, kad suprasti kam jie reikalingi, pirma riekia suprasti kaip saugomi serializuoti objektai faile. Failas yra tik baitų seka, todėl reikalingas būdas vieną objektą atskirti nuo kito. Toks skirtukas dažnai vadinamas prefiksu ir savyje saugo papildomą informaciją, pavyzdžiui, serializuoto objekto dydį:

image

Norint nuskaityti pavyzdyje pateiktą baitų seką pirma nuskaitome prefiksa, o tada tiek baitų kiek nurodyta prefikse. Operaciją kartojame kol nebus nuskaityti visi baitai. Iš kodo pusės objektų įrašymas / nuskaitymas iš failo galėtų atrodyti taip:

[ProtoContract]
public class FakeData
{
    [ProtoMember(1)]
    public int Sequence { get; set; }

    [ProtoMember(2)]
    public string[] Data { get; set; }
}

[Test]
public void BinaryDataExample()
{
    var path = "d:\\Temp.txt";
    var serializer = new ProtocolBuffersStreamSerializer();

    using (var stream = File.Create(path))
    {
        for (int i = 0; i < 10; i++)
        {
            var fakeData = GenerateFakeData(i);
            serializer.Serialize(fakeData, stream);
        }
    }

    using (var stream = File.OpenRead(path))
    {
        FakeData fakeData;
        while ((fakeData = serializer.Deserialize<FakeData>(stream)) != null)
        {
            Console.WriteLine(fakeData.Sequence);
        }
    }
}

private static FakeData GenerateFakeData(int sequence)
{
    var count = new Random().Next(1, 5);
    var fakeData = new FakeData
    {
        Sequence = sequence,
        Data = new string[count]
    };

    for (var i = 0; i < count; i++)
    {
        fakeData.Data[i] = Guid.NewGuid().ToString("N");
    }

    return fakeData;
}

Kaip matote, objektų įrašymas ir nuskaitymas iš failų nėra sudėtingas procesas, bet jis reikalauja tam tikro suvokimo apie binarinių duomenų saugojimą.

Patiko (0)

Rodyk draugams