Grafikus C# – Windows Forms tanfolyam › 8.9 lecke Windows Forms · 8.9 lecke · mentés és betöltés A szépen szerkeszthető, ellenőrzött táblázat addig ér valamit, amíg nem zárod be a programot. A tartós tároláshoz fájlba kell írni az adatokat, és induláskor vissza kell olvasni. Ebben a leckében a leggyakoribb, ember- és Excel-barát formátumot, a CSV-t tanulod meg (a hozzá tartozó apró csapdákkal: pontosvessző, idézőjelek, kódolás, tizedesjel), az Táblázat adatainak mentése és betöltése
DataTable XML-mentését, az importálás hibakezelését (nem veszíthetsz adatot egy hibás fájl miatt), a mentetlen változtatások jelzését és a biztonságos mentést.
A lecke végére
- kiválasztod a helyzethez illő formátumot (CSV, XML, később JSON és adatbázis);
- helyes CSV-t írsz (idézőjelek, pontosvessző,
UTF8Encoding(true),InvariantCulture); - idézőjel-kezelő CSV-sorfeldolgozót írsz, és hibás sorokat jelentesz;
- az importálás teljes beolvasás után cseréli az adatot (nem veszíthetsz adatot);
- a
DataTable-t XML-be mented és olvasod vissza; - jelzed a mentetlen változtatást, megerősítést kérsz kilépéskor, és biztonságosan mentesz.
Mielőtt elkezded
Ehhez a leckéhez a 7.4 és 7.8 lecke (MessageBox, fájlválasztók), a 8.4 lecke (BindingList), a 8.8 lecke (adatellenőrzés) és az 5.6 lecke (FormClosing, Settings) szükséges. A fájlkezelés részleteit a 10. fejezet tárgyalja, itt a File osztály metódusait használjuk.
Milyen formátumba mentsünk?
| Formátum | Előny | Hátrány | Mikor? |
|---|---|---|---|
| CSV (szöveg, pontosvesszővel) | Egyszerű, ember által olvasható, Excelben megnyílik. | Nincs típus (minden szöveg), nincs beágyazás; a speciális karakterekre figyelni kell. | Táblázatos adat, import és export másik programmal. |
| XML | Típust és szerkezetet is tárol, ember által olvasható. | Bőbeszédű, nagy fájl. | Összetettebb adat, DataTable mentése (10.7 lecke). |
| JSON | Tömör, népszerű, objektumok mentésére való. | Táblázat-szerkesztővel nem nyitható. | Objektumok és beállítások (10.6 lecke). |
| SQLite | Lekérdezhető, nagy adatot is kezel, tranzakciók. | Összetettebb kezelés. | Sok adat, több felhasználó, kapcsolatok (3. fejezet). |
Hol legyen a fájl?
A felhasználó által választott fájlt a mentés párbeszédablak adja (7.8 lecke). A program saját adatainak helye a Environment.SpecialFolder.ApplicationData alatti mappa (írási jog, felhasználónként külön). A program mappájába (Program Files) ne írj.
CSV mentése helyesen
A CSV első ránézésre egyszerű: soronként egy rekord, a mezők elválasztóval. A csapdák: (1) a pontosvessző az elválasztó magyar Excelben (a vessző a tizedesjel); (2) ha a mező maga tartalmaz pontosvesszőt, idézőjelet vagy sortörést, idézőjelek közé kell tenni, a benne lévő idézőjelet megkettőzve; (3) az Excel csak BOM-os UTF-8 fájlban ismeri fel az ékezeteket; (4) a számokat és dátumokat rögzített alakban írd (InvariantCulture), hogy más nyelvű gépen is beolvasható legyen:
// egy mező biztonságos kiírása: ha speciális karaktert tartalmaz, idézőjelek közé kerül
private static string CsvMezo(string szoveg)
{
szoveg = szoveg ?? "";
string idezo = ((char)34).ToString(); // az idézőjel (")
if (szoveg.IndexOfAny(new char[] { ';', (char)34, (char)13, (char)10 }) >= 0)
{
szoveg = idezo + szoveg.Replace(idezo, idezo + idezo) + idezo; // a belső idézőjel megkettőzve
}
return szoveg;
}
private void CsvMent(string utvonal)
{
List<string> sorok = new List<string>();
sorok.Add("Kod;Nev;Ar;Keszlet"); // fejléc
foreach (Termek t in lista)
{
sorok.Add(CsvMezo(t.Kod) + ";" + CsvMezo(t.Nev) + ";"
+ t.Ar.ToString(CultureInfo.InvariantCulture) + ";"
+ t.Keszlet.ToString(CultureInfo.InvariantCulture));
}
// UTF8Encoding(true): BOM-mal, hogy az Excel helyesen mutassa az ékezeteket
File.WriteAllLines(utvonal, sorok, new UTF8Encoding(true));
}Mackó "Pu" név a fájlban "Mackó ""Pu""" alakú. Ha ezt kihagyod, az idézőjel megtöri a sort, és a beolvasás hibás mezőket ad (vagy a CSV „injekciót” teszi lehetővé, amikor a mező egy képletet tartalmaz).1250,5 (magyar gép) és 1250.5 (angol gép) különbözik. Rögzített alakban (pont) mentve minden gép beolvassa. Ha a fájlt kizárólag magyar Excelben nyitnák, a CurrentCulture kényelmesebb; ezt a döntést dokumentálni kell.é) mutatja. A saját beolvasó (Encoding.UTF8) a BOM-ot kezeli.Jósolj!
A Nev mező értéke Toll; kék (benne pontosvessző), és a mentés nem idézőjelezi. Hány mezőt lát a beolvasás ebben a sorban?
Jóslat ellenőrzése
Ötöt négy helyett: a név két mezőre bomlik, az ár és a készlet eltolódik (az „ár” a " kék" szöveg lenne). Ezért kell az idézőjelezés mentéskor, és a mezőszám ellenőrzése beolvasáskor.
CSV betöltése: sorfeldolgozás és hibajelentés
A sor.Split(';') a legegyszerűbb, de az idézőjeles mezőkkel nem boldogul. Egy kis állapotgép (idézőjelben vagy kívül) pontosan feldolgozza a sort. A fontosabb elv: az importálás soha ne cseréljen adatot, amíg a teljes fájl feldolgozása nem készült el. Először új listába olvasunk, a hibákat gyűjtjük, és a felhasználó dönt:
// egy CSV-sor mezőkre bontása (az idézőjeles mezők és a kettőzött idézőjel kezelésével)
private static List<string> CsvSor(string sor)
{
List<string> mezok = new List<string>();
StringBuilder aktualis = new StringBuilder();
bool idezoben = false;
for (int i = 0; i < sor.Length; i++)
{
char c = sor[i];
if (idezoben)
{
if (c == (char)34)
{
if (i + 1 < sor.Length && sor[i + 1] == (char)34)
{
aktualis.Append((char)34); // kettőzött idézőjel: egy szó szerinti idézőjel
i++;
}
else
{
idezoben = false; // az idézőjeles rész vége
}
}
else
{
aktualis.Append(c);
}
}
else if (c == (char)34)
{
idezoben = true;
}
else if (c == ';')
{
mezok.Add(aktualis.ToString());
aktualis.Clear();
}
else
{
aktualis.Append(c);
}
}
mezok.Add(aktualis.ToString());
return mezok;
}
private List<Termek> CsvBeolvas(string utvonal, List<string> hibak)
{
List<Termek> uj = new List<Termek>();
string[] sorok = File.ReadAllLines(utvonal, Encoding.UTF8);
if (sorok.Length == 0 || sorok[0].Trim() != "Kod;Nev;Ar;Keszlet")
{
hibak.Add("A fájl nem árlista (a fejléc nem egyezik).");
return uj;
}
for (int i = 1; i < sorok.Length; i++)
{
if (sorok[i].Trim().Length == 0)
{
continue; // üres sor
}
List<string> m = CsvSor(sorok[i]);
decimal ar;
int db;
if (m.Count != 4)
{
hibak.Add((i + 1) + ". sor: 4 mező kellene, " + m.Count + " van.");
}
else if (!decimal.TryParse(m[2], NumberStyles.Number, CultureInfo.InvariantCulture, out ar) || ar <= 0)
{
hibak.Add((i + 1) + ". sor: érvénytelen ár (" + m[2] + ").");
}
else if (!int.TryParse(m[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out db) || db < 0)
{
hibak.Add((i + 1) + ". sor: érvénytelen készlet (" + m[3] + ").");
}
else
{
uj.Add(new Termek { Kod = m[0].Trim(), Nev = m[1].Trim(), Ar = ar, Keszlet = db });
}
}
return uj;
}
private void Megnyit(string utvonal)
{
List<string> hibak = new List<string>();
List<Termek> uj;
try
{
uj = CsvBeolvas(utvonal, hibak);
}
catch (IOException ex)
{
MessageBox.Show(this, "A fájl nem olvasható: " + ex.Message, "Megnyitás", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; // a jelenlegi adat érintetlen
}
if (hibak.Count > 0)
{
string szoveg = hibak.Count + " hibás sor van, az első: " + hibak[0] + Environment.NewLine + "A hibás sorok nélkül (" + uj.Count + " sor) betöltsem?";
if (uj.Count == 0 || MessageBox.Show(this, szoveg, "Importálás", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes)
{
return;
}
}
// csak a teljes feldolgozás után cseréljük az adatot
lista.Clear();
foreach (Termek t in uj)
{
lista.Add(t);
}
}idezoben jelző tárolja, hogy idézőjelben vagyunk-e. Idézőjelben a pontosvessző szó szerinti karakter; a kettőzött idézőjel egy idézőjel. Ez az egyszerű szabály a CSV lényege.i + 1: a fájl sorszáma) és okkal jelentjük, hogy a felhasználó ki tudja javítani a fájlt.ReadAllLines) feltételezi, hogy a mező nem tartalmaz sortörést. A sortörést is tartalmazó mezőhöz folyamatos olvasás kell (StreamReader, 10.2 lecke) vagy kész CSV-könyvtár (NuGet).Jósolj!
A betöltés először törli a listát (lista.Clear()), és utána olvassa be a fájlt, amelynek a 12. sora hibás: a beolvasás kivételt dob. Mi marad a programban?
Jóslat ellenőrzése
Egy félig betöltött vagy üres lista: a régi adat elveszett, az új pedig nem készült el. Ezért olvasunk először új listába, és a régit csak a sikeres feldolgozás után cseréljük.
DataTable mentése XML-be
Ha az adat DataTable-ben van (3.8 és 8.7 lecke), az XML-mentés egy sor: a WriteXml a sorokat és (kérésre) a szerkezetet is kiírja, a ReadXml visszaolvassa:
private void XmlMent(DataTable tabla, string utvonal)
{
tabla.TableName = "Termek"; // kötelező: név nélkül kivételt dob
tabla.WriteXml(utvonal, XmlWriteMode.WriteSchema); // az oszlopok típusát is menti
}
private DataTable XmlBetolt(string utvonal)
{
DataTable uj = new DataTable("Termek");
uj.ReadXml(utvonal); // a séma a fájlban van, az oszlopok létrejönnek
return uj;
}XmlWriteMode.WriteSchema a fájlba írja az oszlopok nevét és típusát (decimal, int), így a beolvasáskor nem lesz minden oszlop szöveg. Séma nélkül (IgnoreSchema) a ReadXml kitalálja az oszlopokat, de mind szöveg lesz.DataTable neve nélkül a WriteXml kivételt dob (nincs beállítva a tábla neve).ReadXml az új táblába olvasson, és a felület csak a sikeres beolvasás után cserélje az adatforrást (a CSV-nél tanult elv).XmlException kivételt dob: try–catch kell, és az eredeti adatot meg kell tartani.Jósolj!
A tábla mentése XmlWriteMode.IgnoreSchema módban történt, a betöltés után az Ár oszlop szerint rendezel. Milyen sorrendet kapsz?
Jóslat ellenőrzése
Szöveg szerinti sorrendet (például 100, 20, 300): séma nélkül minden oszlop string típusú, ezért a rendezés betűrend szerinti. A WriteSchema ezt megelőzi.
Mentetlen változtatás és biztonságos mentés
private string fajl = "";
private bool modosult = false;
private void Modosult()
{
if (!modosult)
{
modosult = true;
CimFrissit();
}
}
// a változás forrásai: sor hozzáadása/törlése, cella módosítása
lista.ListChanged += (s, e) => Modosult();
private void CimFrissit()
{
string nev = fajl.Length == 0 ? "Névtelen" : Path.GetFileName(fajl);
this.Text = nev + (modosult ? "*" : "") + " – Árlista";
}
// biztonságos mentés: ideiglenes fájlba, majd csere (az eredeti nem maradhat félkész)
private void BiztonsagosMent(string utvonal)
{
string ideiglenes = utvonal + ".tmp";
CsvMent(ideiglenes); // ha itt hiba van, az eredeti fájl épen marad
if (File.Exists(utvonal))
{
File.Copy(utvonal, utvonal + ".bak", true); // az előző változat mentése
File.Delete(utvonal);
}
File.Move(ideiglenes, utvonal);
}BindingList.ListChanged esemény jelzi a sor hozzáadását és törlését (és a cella-módosítást, ha az elem INotifyPropertyChanged-et valósít meg, 8.6 lecke). Egyszerű osztályoknál a dgv.CellValueChanged eseményt is kezeld.* jel az ismerős jelzés. Mentés után a modosult = false beállítása és a cím frissítése nem maradhat el..bak az előző változatot őrzi, ha a felhasználó hibás adatot mentett.FormClosing eseményben YesNoCancel kérdés (7.4 lecke); a sikertelen mentés megakadályozza a kilépést.Jósolj!
A mentés közvetlenül az eredeti fájlba ír (File.WriteAllLines(utvonal, ...)), és a lemez a mentés közepén megtelik. Mi marad az eredeti fájlból?
Jóslat ellenőrzése
Egy félig kiírt, csonka fájl: a mentés a fájl tartalmát előbb törli, és az írás megszakad. Az ideiglenes fájlos mentésnél az eredeti érintetlen marad, és a hiba csak az ideiglenes fájlt érinti.
Mit tegyél ide: az árlista program a „* – Árlista” címmel, a Fájl menü és az Importálás hibajelentő ablaka („2 hibás sor van…”).Javasolt fájlnév: winforms-8-9-fajl.webp · Alt szöveg: Árlista mentéssel és importálási hibajelentéssel
Hibakeresés: te vagy a hibadetektív
Mi a baj, és hogyan javítod?
Gondold végig az okot, és csak utána nyisd le a megoldást.
1. Az Excelben hibásak az ékezetek
A Füzet szó helyett Füzet látszik. A fájl BOM nélküli UTF-8, az Excel régi kódlapként értelmezi. Mentsd Ok és javítás
new UTF8Encoding(true) kódolással (BOM-mal).
2. Az Excel egyetlen oszlopba teszi az egész sort
A CSV megnyitásakor nincsenek oszlopok. Magyar Excelben a pontosvessző az elválasztó (a vessző a tizedesjel). Vesszővel mentett fájlnál az Excel nem bontja a mezőket. Használj pontosvesszőt.Ok és javítás
3. A név mezőbe kerül az ár
sorok.Add(t.Kod + ";" + t.Nev + ";" + t.Ar + ";" + t.Keszlet); // a név pontosvesszőt tartalmazhatHiba és javítás
A mezőket nem idézőjelezi. Használd a CsvMezo metódust a szöveges mezőknél, a számokat InvariantCulture alakban.
4. A betöltés magyar gépen jó, angolon nem
A A számot a gép kultúrája szerint írtad/olvastad. Mentsd és olvasd 1250,5 ár az egyik gépen 1250,5, a másikon 12505.Ok és javítás
CultureInfo.InvariantCulture alakban (pont a tizedesjel).
5. A hibás fájl törli az adataimat
A hibás CSV megnyitása után a lista üres. A betöltés előbb törölte a listát. Olvass új listába, ellenőrizz, és csak a sikeres feldolgozás után cserélj.Ok és javítás
6. A kilépéskor elvész a munka
A felhasználó bezárja az ablakot, és a módosítások eltűnnek. Nincs módosítás-jelzés és megerősítés. Használd a Ok és javítás
modosult jelzőt (ListChanged), és a FormClosing eseményben kérdezz (YesNoCancel).
Mini projekt: árlista fájlkezeléssel
Egészítsd ki az árlista-szerkesztőt (8.8 lecke) fájlkezeléssel:
- Fájl menü: Új (Ctrl+N), Megnyitás... (Ctrl+O), Mentés (Ctrl+S), Mentés másként..., Kijelöltek exportálása..., Importálás hozzáfűzéssel..., Kilépés.
- CSV formátum: pontosvessző, fejléc (
Kod;Nev;Ar;Keszlet), BOM-os UTF-8, számInvariantCulturealakban; a szöveges mezők idézőjelezve, ha kell (CsvMezo). - Betöltés:
CsvSorállapotgép; a fejléc ellenőrzése; hibás sorok gyűjtése sorszámmal; hibánál a felhasználó dönt (a hibás sorok nélkül betölti, vagy megszakítja); az adat csak a teljes feldolgozás után cserélődik. - Importálás hozzáfűzéssel: a másik fájl sorait a meglévő listához adja (az ismétlődő kódot kihagyja, és jelzi, hány sort hagyott ki).
- Kijelöltek exportálása: csak a rácsban kijelölt sorokat menti új fájlba.
- Mentetlen változtatás: a
modosultjelző (ListChangedésCellValueChanged), a cím*jele, megerősítés Új, Megnyitás és Kilépés előtt (YesNoCancel), sikertelen mentésnél nem folytat. - Biztonságos mentés: ideiglenes fájl,
.bakaz előző változatról; a mentés előtt az adatellenőrzés (8.8 lecke) fut, hibánál nem ment. - Utolsó mappa és fájl: a
Settings-ben (UtolsoMappa), a fájlválasztók ott kezdenek. - Teszteld: Excelben nyitás (ékezetek, oszlopok), pontosvesszős és idézőjeles név, tizedes ár, üres fájl, rossz fejléc, hibás sorok (2–3 hiba), kihagyott duplikátum, kijelöltek exportja, mentés közbeni hiba (írásvédett fájl), kilépés mentetlenül.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A CsvMezo, CsvSor, CsvBeolvas metódusokat a lecke mutatja. Itt a fájlkezelő menüpontok és a mentetlen adat kezelése áll.
private BindingList<Termek> lista = new BindingList<Termek>();
private string fajl = "";
private bool modosult = false;
private void Form1_Load(object sender, EventArgs e)
{
dgv.AutoGenerateColumns = false;
dgv.DataSource = lista;
lista.ListChanged += (s, ev) => Modosult();
string mappa = Properties.Settings.Default.UtolsoMappa;
if (Directory.Exists(mappa))
{
ofd.InitialDirectory = mappa;
sfd.InitialDirectory = mappa;
}
ofd.Filter = sfd.Filter = "Árlista (*.csv)|*.csv|Minden fájl (*.*)|*.*";
sfd.DefaultExt = "csv";
CimFrissit();
}
private void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
Modosult();
}
private void Modosult()
{
if (!modosult)
{
modosult = true;
CimFrissit();
}
}
private void CimFrissit()
{
string nev = fajl.Length == 0 ? "Névtelen" : Path.GetFileName(fajl);
this.Text = nev + (modosult ? "*" : "") + " – Árlista";
}
private bool Ment(bool maskent)
{
DataGridViewCell elso;
if (MindEllenoriz(out elso) > 0) // a 8.8 lecke ellenőrzése
{
MessageBox.Show(this, "A táblázatban hibák vannak. A mentés előtt javítsd őket.", "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Warning);
dgv.CurrentCell = elso;
return false;
}
string ut = fajl;
if (maskent || ut.Length == 0)
{
sfd.FileName = ut.Length == 0 ? "arlista" : Path.GetFileName(ut);
if (sfd.ShowDialog(this) != DialogResult.OK)
{
return false;
}
ut = sfd.FileName;
}
try
{
BiztonsagosMent(ut);
fajl = ut;
modosult = false;
CimFrissit();
Properties.Settings.Default.UtolsoMappa = Path.GetDirectoryName(ut);
Properties.Settings.Default.Save();
tsslUzenet.Text = "Mentve: " + Path.GetFileName(ut);
return true;
}
catch (Exception ex)
{
MessageBox.Show(this, "A mentés nem sikerült." + Environment.NewLine + ex.Message, "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
private bool Folytathat()
{
if (!modosult)
{
return true;
}
switch (MessageBox.Show(this, "Mented a változtatásokat?", "Árlista", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning))
{
case DialogResult.Yes: return Ment(false);
case DialogResult.No: return true;
default: return false;
}
}
private void miUj_Click(object sender, EventArgs e)
{
if (Folytathat())
{
lista.Clear();
fajl = "";
modosult = false;
CimFrissit();
}
}
private void miMegnyit_Click(object sender, EventArgs e)
{
if (Folytathat() && ofd.ShowDialog(this) == DialogResult.OK)
{
// a lecke Megnyit metódusát alakítsd bool visszatérésűvé: false hibánál vagy megszakításnál, true siker esetén
if (Megnyit(ofd.FileName))
{
fajl = ofd.FileName;
modosult = false;
CimFrissit();
}
}
}
private void miImport_Click(object sender, EventArgs e)
{
if (ofd.ShowDialog(this) != DialogResult.OK) { return; }
List<string> hibak = new List<string>();
List<Termek> uj;
try { uj = CsvBeolvas(ofd.FileName, hibak); }
catch (IOException ex) { MessageBox.Show(this, ex.Message, "Importálás", MessageBoxButtons.OK, MessageBoxIcon.Error); return; }
int kihagyott = 0;
foreach (Termek t in uj)
{
bool van = false;
foreach (Termek m in lista) { if (string.Equals(m.Kod, t.Kod, StringComparison.OrdinalIgnoreCase)) { van = true; break; } }
if (van) { kihagyott++; } else { lista.Add(t); }
}
tsslUzenet.Text = (uj.Count - kihagyott) + " sor hozzáfűzve, " + kihagyott + " ismétlődő kihagyva, " + hibak.Count + " hibás sor.";
}
private void miExport_Click(object sender, EventArgs e)
{
if (dgv.SelectedRows.Count == 0) { return; }
if (sfd.ShowDialog(this) != DialogResult.OK) { return; }
List<string> sorok = new List<string>();
sorok.Add("Kod;Nev;Ar;Keszlet");
foreach (DataGridViewRow r in dgv.SelectedRows)
{
Termek t = r.DataBoundItem as Termek;
if (t != null)
{
sorok.Add(CsvMezo(t.Kod) + ";" + CsvMezo(t.Nev) + ";" + t.Ar.ToString(CultureInfo.InvariantCulture) + ";" + t.Keszlet.ToString(CultureInfo.InvariantCulture));
}
}
File.WriteAllLines(sfd.FileName, sorok, new UTF8Encoding(true));
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing && !Folytathat())
{
e.Cancel = true;
}
}Mit tegyél ide: az árlista program megnyitott fájllal, a menü és a státuszüzenet.Javasolt fájlnév: winforms-8-9-kesz.webp · Alt szöveg: Az árlista program fájlkezeléssel
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. Fejléc a DataGridView-ból
A CSV fejlécét ne kézzel írd, hanem a rács oszlopaiból állítsd elő ( A HeaderText vagy DataPropertyName). Melyiket használod a fájlban, és miért?Egy lehetséges megoldás
DataPropertyName (stabil, kódban használt név); a HeaderText a felhasználónak szánt felirat, amely nyelvváltáskor módosulhat, a fájlformátumnak pedig állandónak kell lennie.List<string> fejlec = new List<string>();
foreach (DataGridViewColumn o in dgv.Columns)
{
if (o.Visible)
{
fejlec.Add(o.DataPropertyName);
}
}
string fejlecSor = string.Join(";", fejlec);
2. Automatikus mentés
Egy időzítő 2 percenként Az időzítő a .autosave fájlba menti a módosított adatot (az eredeti fájlt nem írja felül). Indításkor, ha ilyen fájl van, felajánlja a visszaállítást. Hogyan oldod meg?Egy lehetséges megoldás
modosult jelző esetén ment az ideiglenes fájlba; a rendes mentés és az új dokumentum törli. Induláskor a File.Exists alapján kérdez (YesNo).private string AutoUtvonal(){
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Arlista", "auto.csv");
}
private void tmrAuto_Tick(object sender, EventArgs e)
{
if (modosult)
{
Directory.CreateDirectory(Path.GetDirectoryName(AutoUtvonal()));
CsvMent(AutoUtvonal());
}
}
3. Fejléc szerinti oszlopsorrend
A beolvasás ne függjön az oszlopok sorrendjétől: a fejléc nevei szerint keresse meg az oszlopokat ( A fejlécből szótárt építesz (oszlopnév → index), és a mezőket név szerint éred el.Nev;Kod;Keszlet;Ar sorrend is jó). Hogyan?Egy lehetséges megoldás
Dictionary<string, int> index = new Dictionary<string, int>();
List<string> fejlec = CsvSor(sorok[0]);
for (int i = 0; i < fejlec.Count; i++)
{
index[fejlec[i].Trim()] = i;
}
// használat egy sorban:
string nev = m[index["Nev"]];
string kod = m[index["Kod"]];
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Mezők idézőjelezése
Írd meg a fájlba kerülő alakot: (a) (a) Toll; (b) Toll; kék; (c) Mackó "Pu".Megoldás megnyitása
Toll (nem kell idézőjel); (b) "Toll; kék"; (c) "Mackó ""Pu""" (a belső idézőjel kettőzve).
2. Formátumválasztás
Melyik formátumot választod: (a) szülők névsora Excelbe; (b) program beállításai; (c) 10 000 sor lekérdezhető adat? (a) CSV; (b) JSON vagy a Megoldás megnyitása
Settings (10.6 és 10.8 lecke); (c) SQLite (3. fejezet).
Közepes szint
3. Dátum a CSV-ben
Az árlista kapjon Rögzített alakban: Datum mezőt. Milyen alakban írod a fájlba, és hogyan olvasod vissza?Megoldás megnyitása
yyyy-MM-dd (InvariantCulture), olvasáskor DateTime.TryParseExact ugyanezzel az alakkal. Így nem függ a gép beállításától.string szoveg = t.Datum.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
DateTime d;
bool ok = DateTime.TryParseExact(m[4], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out d);
4. Vessző az elválasztó
Egy külföldi partner fájlja vesszővel elválasztott. Hogyan teszed állíthatóvá az elválasztót? Az elválasztó karakter paraméter (Megoldás megnyitása
char elvalaszto) a CsvMezo, CsvSor metódusokban (alapértelmezett ;); a felhasználó egy legördülő listából választja, vagy a fájl első soráról kitalálod (sep=; sor, vagy a gyakoribb karakter).
Kihívás szint
5. Nagy fájl folyamatosan
Egy 500 MB-os CSV-t nem olvashatsz be egyszerre. Hogyan olvasod soronként, és mit mutatsz a felhasználónak? Az Megoldás megnyitása
File.ReadLines (halasztott, soronkénti) vagy StreamReader olvas; a haladást (ProgressBar) a feldolgozott bájtok arányából számolod. A hosszú művelet nem futhat a felület szálán (11. fejezet).int szam = 0;foreach (string sor in File.ReadLines(utvonal, Encoding.UTF8)){
szam++;
// a sor feldolgozása (CsvSor), a memóriában csak az aktuális sor van
}
6. Adat-verzió a fájlban
A program új változata új oszlopot kap. Hogyan olvassa a régi fájlokat is? Mit teszel a fájl első soraiba? A fájlformátum verziószámot kap (Megoldás megnyitása
#verzio=2 első sor, vagy fejléc-ellenőrzés). A beolvasó a régebbi verziókhoz alapértelmezett értéket tölt az új oszlopra. A fájlformátumot dokumentálni kell.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A pontosvessző, mert a vessző a tizedesjel. Ha pontosvesszőt, idézőjelet vagy sortörést tartalmaz; a belső idézőjelet kettőzni kell. A BOM jelzi az Excelnek az UTF-8 kódolást, így az ékezetek helyesek. Hogy hibás fájl ne törölhesse a meglévő adatot: a régit csak a sikeres feldolgozás után cseréled. Az oszlopok típusát is menti, így a betöltés után nem lesz minden szöveg. Ideiglenes fájlba írsz, és csak siker után cseréled az eredetit (és őrzöl .bak másolatot).1. Mi a CSV elválasztója magyar Excelben, és miért?
2. Mikor kell idézőjelbe tenni egy mezőt?
3. Miért UTF8Encoding(true)?
4. Miért új listába olvasol betöltéskor?
5. Mire való az XmlWriteMode.WriteSchema?
6. Hogyan mentesz biztonságosan?
7. Honnan tudod, hogy a dokumentum módosult?
ListChanged és CellValueChanged események alapján egy modosult jelző; megjelenítése a címben (*).
Mit tudsz már?
- Kiválasztod a megfelelő tárolási formátumot, és helyes CSV-t írsz és olvasol.
- Hibás sorokat jelentesz, és az importálás nem veszít adatot.
- A
DataTable-t XML-be mented és olvasod vissza. - Jelzed a mentetlen változtatást, és biztonságosan mentesz.
- Felismered a tipikus hibákat (BOM, elválasztó, kultúra, idézőjelek).
Következő lecke: mini projekt – táblázatos nyilvántartás
A 8. fejezet záró projektje: egy teljes táblázatos nyilvántartás keresővel, ellenőrzéssel, fájlkezeléssel és összesítéssel.




