Programozás során ritkán fordul az elő, hogy az adatokat egymás után következő bitek sorozataként kell kezelni. Általában az adatok blokkokba rendezve jelentenek valami érdekeset, programozási nyelvvel is érdemben kezelhetőt. Azonban minden ilyen blokkművelet visszavezethető bájtok és azon belül is bitek írásának és olvasásának sorozatára.
Ezeket a transzformációkat nem kell nekünk kézzel megírnunk, mert C# esetén erre vannak a különböző Writer és Reader osztályok. Bináris fájlok kezelésére a BinaryWriter és BinaryReader osztály alkalmazható, amiknek a segítségével bármilyen C# alaptípust ki tudunk írni és be tudunk olvasni bináris formában.
Nézzünk egy példát:
using System;
using System.IO;
using System.Text;
namespace PeldaBinarisFajlkezeles2
{
public class Osztaly
{
public int Egesz { get; set; }
public double Tort { get; set; }
public override string ToString()
{
return $"Egesz: {Egesz}, Tort: {Tort}";
}
}
class Program
{
private static void Kiir(BinaryWriter target, Osztaly peldany)
{
target.Write(peldany.Egesz);
target.Write(peldany.Tort);
}
private static Osztaly Beolvas(BinaryReader source)
{
return new Osztaly
{
Egesz = source.ReadInt32(),
Tort = source.ReadDouble()
};
}
static void Main(string[] args)
{
using (MemoryStream teszt = new MemoryStream())
{
Osztaly kiirando = new Osztaly
{
Egesz = 42,
Tort = 3.1415
};
Console.WriteLine("Írás előtt:");
Console.WriteLine(kiirando);
using (BinaryWriter iro = new BinaryWriter(teszt, Encoding.UTF8, true))
{
Kiir(iro, kiirando);
}
//visszaállunk a stream elejére
//az írás után a végén vagyunk
Console.WriteLine("Olvasás után: ");
teszt.Seek(0, SeekOrigin.Begin);
using (BinaryReader olvaso = new BinaryReader(teszt, Encoding.UTF8, true))
{
Osztaly beolvasott = Beolvas(olvaso);
Console.WriteLine(beolvasott);
}
}
Console.ReadKey();
}
}
}
A program kimenete:
Írás előtt:
Egesz: 42, Tort: 3,1415
Olvasás után:
Egesz: 42, Tort: 3,1415
A tesztosztályunk esetén kreatív voltam és Osztaly-nak neveztem el. Ez egy egész és egy double típusú számot tárol, illetve rendelkezik egy ToString implementációval, hogy könnyen ki lehessen írni a konzolra.
A program definiál két segédmetódust, amit ismételten kreatív módon Kiir és Beolvas-nak neveztem el.
A Kiir metódus két paramétert vár. Egy cél BinaryWriter osztályt, aminek a segítségével ír, illetve egy példányt az Osztaly típusból.
A BinaryWriter legfontosabb metódusa a Write, ami rendelkezik az alap típusok kiírására szolgáló felüldefiniált változatokkal.
A Beolvas metódus egyetlen paramétere egy BinaryReader osztály, amiből olvas. Visszatérési értéke pedig egy Osztaly típusú változó.
A BinaryReader számos metódussal rendelkezik. Minden .NET alap típus beolvasásához egy metódussal. Ezek mindegyike Read névvel kezdődik.
A Fő metódus létrehoz egy példányt az Osztaly típusból, amit egy BinaryWriter segítségével beleír egy MemoryStream-be, majd egy BinaryReader segítségével onnan visszaolvassa azt.
A MemoryStream ugyanúgy viselkedik, mint egy normál fájl Stream (mivel ugyanabból az ős Stream osztályból öröklődik), csak éppen a memóriában tárolja a tartalmát.
Itt érdemes megjegyezni, hogy a MemoryStream használata nagy mennyiségű (több MiB) adat tárolására, majd módosítására nem javasolt, mivel a programmemória területét töredezetté teszi és a GC sem tudja hatékonyan töredezettség mentesíteni, ha a tartalma folyamatosan változik.
Érdekesség a BinaryReader és BinaryWriter osztályok példányosításánál található. Az első paraméter minden esetben egy Stream, amiből olvasunk vagy írunk. A második paraméter a szövegek esetén a karakter kódolást határozza meg, a harmadik paraméter pedig azt, hogy a using blokk végén csak a Reader vagy Writer szűnjön meg.
Erre azért van szükség, mert a Reader és Writer osztályok Dispose esetén az alattuk lévő Stream típuson is végrehajtják a Dispose utasítást, hacsak nem kötjük ki, hogy ne tegyék meg.
A karakterkódolást a System.Text névtérben található Encodingosztály statikus tulajdonságai alapján tudjuk meghatározni. Lehetséges értékek:
Enoding.ASCII
Enoding.UTF8
Enoding.UTF16
Enoding.UTF32