Pliki binarne a struktury i listy
Tym tematem nawiążę do sposobu zachowywania danych i ich odczytywania z pliku. Tym razem dane nie będą zapisywane w pliku tekstowym lecz w pliku binarnym.
Plik binarny, to plik (za wyjątkiem plików tekstowych) o dowolnej zawartości, zapisany ustalonym sposobem kodowania. W naszym przypadku będzie to struktura. Poprawny odczyt takiego pliku wymaga znajomości metody kodowania zapisu. W tym rozwiązaniu będzie to znajomość struktury zapisu.
Przykładowy widok na podgląd zdefiniowanego pliku binarnego
Serializacja danych
Proces serializacji danych, to proces zamiany rozbudowanych obiektów (struktur, obiektów) na ciąg (strumień) bajtów, który można zapisać do pliku lub przesyłać przez sieć.
Deserializacja danych
Deserializacja danych jest odwrotnym procesem do serializacji. Deserializacja danych wykonywana jest przy odczycie strumienia bajtów i odtworzeniu do pierwotnej struktury lub pierwotnego obiektu.
Projekt formatki
Układ formatki jak i podstawowy kod tworzonej aplikacji desktopowej jest rozwinięciem aplikacji z tego tematu: Operacje na liście
Do projektu dodamy kolejne komponenty:
- MenuStrip
- OpenFileDialog
- SaveFileDialog
Do kodu dodamy dwie biblioteki, dzięki którym obsłużymy pliki i binarny strumień danych
Wskazówka:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
Serializacja danych
Do naszej struktury dopisujemy dyrektywę serializacji danych. Patrz poniżej
Wskazówka:
[Serializable]
struct Dane
{
public string nazwisko;
public string imie;
public string klasa;
}
Zapis pliku binarnego
Tworzony plik binarny będzie zawierać pełną listę utworzoną w aplikacji. Dla czytelności kodu zapis danych będzie zawsze odbywał się w nowym pliku lub nadpisie już istniejącego pliku.
W pseudokodzie instrukcje zapisu pliku binarnego można podać jak poniżej
- Przygotuj strumień plikowy
- Przygotuj bufor binarnego formatu
- Serializuj dane do bufora
- Zamknij strumień plikowy
Dla naszych binarnych plików zdefiniujemy nowe rozszerzenie. Ja zdefiniowałem rozszerzenie o nazwie: moj. Rozszerzenie zapisałem w filtrze kontrolki okna dialogowego zapisu plików.
W rzeczywistym kodzie w języku C# zorganizujemy to jak poniżej
Wskazówka:
private void zapiszToolStripMenuItem_Click(object sender, EventArgs e)
{
//ustaw filtr plików
saveFileDialog1.Filter = "Pliki binarne .moj|*.moj|Wszystkie pliki *.*|*.*";
//ustaw tytuł okna zapisu dialogowego
saveFileDialog1.Title = "Zapisz zawartość";
//narzuć filtr podstawowy
saveFileDialog1.DefaultExt = "moj";
//ustaw zapis w katalogu exe'ka
saveFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath);
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
FileStream plikStrumienia = new FileStream(saveFileDialog1.FileName,
FileMode.Create,
FileAccess.Write,
FileShare.None);
BinaryFormatter buforDanych = new BinaryFormatter();
buforDanych.Serialize(plikStrumienia, lista);
plikStrumienia.Close();
toolStripStatusLabel1.Text = saveFileDialog1.FileName;
}
}
Skompiluj program. Dodaj do listy kilka elementów i zapisz ją do pliku. Prawidłowo działająca aplikacji utworzy plik, którego zawartość możesz podglądnąć w notatniku.
Odczyt danych z pliku binarnego
Odczyt danych z pliku binarnego w pseudokodzie można przedstawić jak poniżej
- Przygotuj strumień plikowy
- Przygotuj bufor binarnego formatu
- Deserializuj dane do bufora
- Prześlij dane z bufora do listy
- Zamknij strumień plikowy
Otwarcie i odczyt danych z pliku binarnego w rzeczywistym kodzie programu w języku C# można wykonać jak poniżej
Wskazówka:
private void otworzToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Pliki binarne .moj|*.moj|Wszystkie pliki *.*|*.*";
//ustaw tytuł okna zapisu dialogowego
openFileDialog1.Title = "Otwórz plik";
//narzuć filtr podstawowy
openFileDialog1.DefaultExt = "moj";
//ustaw w katalogu exe'ka
openFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath);
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
textBox4.Clear();
FileStream plikStrumienia = File.OpenRead(openFileDialog1.FileName);
BinaryFormatter buforDanych = new BinaryFormatter();
//int id = 0;
lista.Clear();
lista = (List<Dane>)buforDanych.Deserialize(plikStrumienia);
plikStrumienia.Close();
}
}
Po wczytaniu danych z pliku do listy, możemy zawartość listy przesłać do kontrolki TextBox pracującej w układzie wielu linii.
Wczytana lista
Pełny kod aplikacji
Wskazówka:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
struct Dane
{
public string nazwisko;
public string imie;
public string klasa;
}
namespace plik_binarny_struktury_listy
{
public partial class Form1 : Form
{
private Dane d;
private List<Dane> lista = new List<Dane>();
private int idBiezacy = 0;
public Form1()
{
InitializeComponent();
}
private void czyscDana()
{
d.nazwisko = "";
d.imie = "";
d.klasa = "";
}
private void dodaj()
{
czyscDana();
d.nazwisko = textBox1.Text;
d.imie = textBox2.Text;
d.klasa = textBox3.Text;
lista.Add(d);
idBiezacy = lista.Count - 1;
}
private void pokazRekord(int id, TextBox tb)
{
if (id < 0 || id>lista.Count-1) return;
d = lista[id];
tb.Clear();
tb.AppendText((id+1).ToString() + Environment.NewLine);
tb.AppendText(d.nazwisko + Environment.NewLine);
tb.AppendText(d.imie + Environment.NewLine);
tb.AppendText(d.klasa + Environment.NewLine);
}
private void pokazListe(TextBox tb)
{
tb.Clear();
for(int i = 0; i < lista.Count;i++)
{
tb.AppendText((i+1).ToString()+"*******************"+ Environment.NewLine);
tb.AppendText(lista[i].nazwisko + Environment.NewLine);
tb.AppendText(lista[i].imie + Environment.NewLine);
tb.AppendText(lista[i].klasa + Environment.NewLine);
}
}
private void Button1_Click(object sender, EventArgs e)
{
dodaj();
toolStripStatusLabel1.Text = lista.Count.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
pokazListe(textBox4);
}
private void button3_Click(object sender, EventArgs e)
{
pokazRekord(idBiezacy, textBox4);
}
private void button5_Click(object sender, EventArgs e)
{
//pokaz kolejny rekord z lewej
idBiezacy--;
if (idBiezacy < 0) idBiezacy = 0;
pokazRekord(idBiezacy, textBox4);
}
private void textBox5_KeyUp(object sender, KeyEventArgs e)
{
if (textBox5.Text.Length < 1)
{
idBiezacy = 0;
textBox5.Text = "1";
}
else
{
idBiezacy = Convert.ToInt16(textBox5.Text) - 1;
}
pokazRekord(idBiezacy, textBox4);
}
private void button4_Click(object sender, EventArgs e)
{
//pokaz pierwszy rekord
idBiezacy = 0;
pokazRekord(idBiezacy, textBox4);
}
private void button7_Click(object sender, EventArgs e)
{
//pokaz ostatni rekord
idBiezacy = lista.Count-1;
pokazRekord(idBiezacy, textBox4);
}
private void button6_Click(object sender, EventArgs e)
{
//pokaz kolejny rekord z prawej
idBiezacy++;
if (idBiezacy >lista.Count-1) idBiezacy = lista.Count - 1;
pokazRekord(idBiezacy, textBox4);
}
private void button9_Click(object sender, EventArgs e)
{
lista.Clear();
toolStripStatusLabel1.Text = lista.Count.ToString();
}
private void button8_Click(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
textBox3.Clear();
}
private void button10_Click(object sender, EventArgs e)
{
lista.RemoveRange(idBiezacy, 1);
toolStripStatusLabel1.Text = lista.Count.ToString();
}
private void zapiszToolStripMenuItem_Click(object sender, EventArgs e)
{
//ustaw filtr plików
saveFileDialog1.Filter = "Pliki binarne .moj|*.moj|Wszystkie pliki *.*|*.*";
//ustaw tytuł okna zapisu dialogowego
saveFileDialog1.Title = "Zapisz zawartość";
//narzuć filtr podstawowy
saveFileDialog1.DefaultExt = "moj";
//ustaw w katalogu exe'ka
saveFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath);
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
FileStream plikStrumienia = new FileStream(saveFileDialog1.FileName,
FileMode.Create,
FileAccess.Write,
FileShare.None);
BinaryFormatter buforDanych = new BinaryFormatter();
buforDanych.Serialize(plikStrumienia, lista);
plikStrumienia.Close();
toolStripStatusLabel1.Text = saveFileDialog1.FileName;
}
}
private void otworzToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "Pliki binarne .moj|*.moj|Wszystkie pliki *.*|*.*";
//ustaw tytuł okna zapisu dialogowego
openFileDialog1.Title = "Otwórz plik";
//narzuć filtr podstawowy
openFileDialog1.DefaultExt = "moj";
//ustaw w katalogu exe'ka
openFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath);
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
textBox4.Clear();
FileStream plikStrumienia = File.OpenRead(openFileDialog1.FileName);
BinaryFormatter buforDanych = new BinaryFormatter();
//int id = 0;
lista.Clear();
lista = (List<Dane>)buforDanych.Deserialize(plikStrumienia);
plikStrumienia.Close();
}
}
}
}