Grafikus C# – Windows Forms tanfolyam › 10.1 lecke

Windows Forms · 10.1 lecke · fájlrendszer

File, Path és Directory

A programok adatai csak addig élnek, amíg a program fut: a tartós tároláshoz a fájlrendszerrel kell dolgozni. Eddig a fájlokat a párbeszédablakokon át, néhány File metódussal kezelted (2.2, 7.8, 8.9 lecke). A 10. fejezet ezt rendszerbe foglalja. Az első lecke a fájlrendszer három alaposzlopa: a File és a FileInfo (fájlok), a Directory és a DirectoryInfo (mappák) és a Path (útvonalak összeállítása és bontása). Megtanulod a biztonságos útvonalépítést, az érvénytelen fájlnevek kezelését, a mappák bejárását és törlését, és a relatív útvonalak csapdáit.

  • kb. 100 perc
  • közepes szint
  • File, Path, Directory
Diák C# programozást gyakorol egy tanulósarokban

A lecke végére

  • a File statikus metódusait használod (létezés, olvasás, írás, másolás, áthelyezés, törlés);
  • megkülönbözteted a File és a FileInfo osztályt, és lekérdezed a fájl adatait;
  • útvonalat építesz és bontasz a Path osztállyal (Combine, GetFileName, GetExtension…), kézi összefűzés nélkül;
  • érvénytelen karaktereket tartalmazó fájlnevet tisztítasz meg;
  • mappát hozol létre, listázol és törölsz (Directory, EnumerateFiles);
  • érted a relatív útvonalak működését, és tudod, hová írhat a program;
  • kezeled a tipikus kivételeket (UnauthorizedAccessException, DirectoryNotFoundException).

Mielőtt elkezded

Ehhez a leckéhez a 7.4 és 7.8 lecke (MessageBox, fájlválasztók), a 4.7 lecke (try–catch alapjai) és az 5.6 lecke (az alkalmazás életciklusa) szükséges.

9.10 Rajzolóprogram →

File és FileInfo

A File statikus osztály: minden metódusa útvonalat kap. Egyszerű, egyszeri műveletekre (olvas, ír, másol) ez a legkényelmesebb. A FileInfo egy konkrét fájlt reprezentáló objektum, amelynek tulajdonságai (méret, dátum) és metódusai vannak; többszöri használatnál hatékonyabb:

Form1.cs (using System.IO;)C#
string ut = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "jegyzet.txt");

// létezés, írás, olvasás
if (!File.Exists(ut))
{
File.WriteAllText(ut, "Első sor" + Environment.NewLine); // létrehozza, vagy felülírja
}

File.AppendAllText(ut, "Újabb sor" + Environment.NewLine); // a végére fűz
string mind = File.ReadAllText(ut);
string[] sorok = File.ReadAllLines(ut);

// másolás, áthelyezés (átnevezés is), törlés
File.Copy(ut, ut + ".bak", true); // a true: felülírhatja a célt (különben IOException)
File.Move(ut + ".bak", ut + ".reg"); // a cél nem létezhet (különben IOException)
File.Delete(ut + ".reg"); // nem létező fájlnál nem dob hibát

// FileInfo: a fájl adatai
FileInfo info = new FileInfo(ut);

if (info.Exists)
{
lblInfo.Text = info.Name + " – " + info.Length + " bájt, módosítva: " + info.LastWriteTime.ToString("yyyy. MM. dd. HH:mm")
+ ", kiterjesztés: " + info.Extension + ", írásvédett: " + info.IsReadOnly;
}
File vagy FileInfo?Egyszeri műveletre a File egyszerűbb. Ha több adatot kérdezel le ugyanarról a fájlról (méret, dátum, név), a FileInfo kényelmesebb. A FileInfo adatai a létrehozás pillanatában gyorsítótárazódnak: frissítéshez Refresh() kell.
FelülírásA WriteAllText a meglévő fájlt felülírja, figyelmeztetés nélkül. A Copy(forrás, cél, true) is. Felhasználói adatnál előbb kérdezz (7.4 lecke), vagy az OverwritePrompt segít (7.8 lecke).
Move = átnevezésAzonos mappán belüli Move a fájlt átnevezi. A cél nem létezhet. Más meghajtóra másolás + törlés történik (lassabb).
DátumokA CreationTime, LastWriteTime, LastAccessTime helyi idő; az …Utc változatok egyetemes idő. A LastWriteTime a „mikor módosult” kérdésre a megfelelő.

Jósolj!

A jegyzet.txt létezik, tartalma Fontos adat. A kód a File.WriteAllText("jegyzet.txt", "Új szöveg") hívást hajtja végre. Mi lesz a fájl tartalma?

Jóslat ellenőrzése

Csak az Új szöveg: a WriteAllText a meglévő tartalmat felülírja, nem fűz hozzá. A hozzáfűzéshez AppendAllText kell. Ez az egyik leggyakoribb adatvesztő hiba.

Path: útvonalak építése és bontása

Form1.csC#
string ut = @"C:\Felhasznalok\Anna\Dokumentumok\jelentes.2026.docx";

Path.GetFileName(ut); // jelentes.2026.docx
Path.GetFileNameWithoutExtension(ut); // jelentes.2026
Path.GetExtension(ut); // .docx (ponttal)
Path.GetDirectoryName(ut); // C:\Felhasznalok\Anna\Dokumentumok
Path.ChangeExtension(ut, ".pdf"); // ...\jelentes.2026.pdf

// építés: SOHA ne fűzd össze kézzel (a \ jel hiányozhat vagy duplázódhat)
string mappa = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string adat = Path.Combine(mappa, "Jegyzetek", "jegyzetek.json"); // több tag is lehet

// ideiglenes fájl és mappa
string ideiglenes = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".tmp");

// teljes útvonal (a relatívból)
string teljes = Path.GetFullPath(@"adatok\naplo.txt");

// érvénytelen karakterek a fájlnévben: \ / : * ? " < > | és a vezérlőkarakterek
private string FajlnevTisztit(string nev)
{
foreach (char c in Path.GetInvalidFileNameChars())
{
nev = nev.Replace(c, '_');
}

nev = nev.Trim().TrimEnd('.'); // a Windows nem szereti a pontra végződő nevet

return nev.Length == 0 ? "nevtelen" : nev;
}
CombineA Path.Combine a megfelelő elválasztót (\ Windows alatt) teszi a tagok közé. Ha egy későbbi tag abszolút útvonal (C:\... vagy \ jellel kezdődik), az eldobja az előzőket: a felhasználótól kapott mappanevet így ne fűzd mappa-elem után bizalommal (útvonal-bejárás, lásd lent).
GetExtension és a pontA kiterjesztést ponttal adja (.docx). Összehasonlításhoz használj kis- és nagybetűtől független összehasonlítást: string.Equals(a, b, StringComparison.OrdinalIgnoreCase) (a Windows fájlnevei nem érzékenyek a betűnagyságra).
Érvénytelen karakterekA GetInvalidFileNameChars a tiltott karaktereket adja. A felhasználó által megadott név (például egy jegyzet címe) mindig tisztítsd meg, mielőtt fájlnévnek használod.
Útvonal-bejárásA felhasználótól vagy fájlból kapott "..\..\Windows" típusú név kijuthat az engedélyezett mappából. Ellenőrizd: Path.GetFullPath(Path.Combine(alap, nev)) StartsWith(alap)-e.

Jósolj!

A Path.Combine(@"C:\Adatok", @"D:\Masik\fajl.txt") mit ad vissza?

Jóslat ellenőrzése

A D:\Masik\fajl.txt értéket: a második tag abszolút útvonal, ezért eldobja az elsőt. Ha a második tag felhasználói bemenet, ez biztonsági kérdés: a program a megadott mappán kívülre írhat.

Directory és DirectoryInfo

Form1.csC#
// létrehozás: a köztes mappákat is létrehozza; ha már létezik, nem hiba
Directory.CreateDirectory(Path.Combine(mappa, "Jegyzetek", "Mentesek"));

bool van = Directory.Exists(mappa);

// listázás
string[] fajlok = Directory.GetFiles(mappa, "*.txt", SearchOption.TopDirectoryOnly); // csak ebben a mappában
string[] almappak = Directory.GetDirectories(mappa);

// lusta bejárás: nagy mappánál az eredmény azonnal jön, nem várja meg a teljes listát
foreach (string f in Directory.EnumerateFiles(mappa, "*.*", SearchOption.TopDirectoryOnly))
{
lstFajlok.Items.Add(Path.GetFileName(f));
}

// mappa mérete: rekurzív összegzés, védett mappák kihagyásával
private long MappaMeret(string ut)
{
long ossz = 0;

try
{
foreach (string f in Directory.EnumerateFiles(ut)) { ossz += new FileInfo(f).Length; }

foreach (string d in Directory.EnumerateDirectories(ut)) { ossz += MappaMeret(d); }
}
catch (UnauthorizedAccessException) { } // a védett mappát kihagyjuk
catch (DirectoryNotFoundException) { }

return ossz;
}

// törlés: üres mappa, vagy teljes tartalommal (VESZÉLYES: nincs szemétkosár!)
Directory.Delete(ures);
Directory.Delete(teli, true); // rekurzív: mindent töröl
GetFiles és EnumerateFilesA GetFiles a teljes listát tömbként adja (megvárja a bejárást), az EnumerateFiles lustán, soronként: nagy mappánál gyorsabban indul, és a bejárás megszakítható. Kisebb mappához mindkettő jó.
SearchOptionA AllDirectories az almappákat is bejárja. Védett mappa (például a rendszermappák) UnauthorizedAccessException kivételt dob, ami a teljes bejárást megszakítja: saját rekurzív bejárás (lásd fent) kezelheti.
Delete rekurzívanA Directory.Delete(ut, true) mindent töröl, a szemétkosár megkerülésével. Felhasználói adatnál megerősítés kell (YesNo, Button2, 7.4 lecke), és a mappa útvonalát ellenőrizd (üres vagy gyökér útvonalnál ne fusson).
SzemétkosárA .NET beépítetten nem küld fájlt a szemétkosárba. A Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(…, RecycleOption.SendToRecycleBin) megteszi (hivatkozás a Microsoft.VisualBasic szerelvényre), ez a megerősítés mellett biztonságosabb választás.

Jósolj!

A Directory.GetFiles(@"C:\", "*.*", SearchOption.AllDirectories) hívás hogyan viselkedik egy átlagos felhasználónál?

Jóslat ellenőrzése

Hosszú ideig fut (a felület lefagy), és UnauthorizedAccessException kivétellel leáll az első védett mappánál (például System Volume Information), mielőtt visszaadná az eredményt. Nagy fát saját, kivételt kezelő rekurzióval és háttérszálon (11. fejezet) járj be.

Relatív útvonalak és hová írhat a program

A "adatok.txt" relatív útvonal a program munkamappájához viszonyul, amely nem biztos, hogy a program mappája: a fájlválasztók (RestoreDirectory = false esetén) és a parancsikon beállítása is megváltoztathatja. Ezért a program adatait sosem relatív útvonalra írd:

CélHová?Hogyan?
Felhasználó dokumentumaA felhasználó által választott hely (Dokumentumok)Fájlválasztó, SpecialFolder.MyDocuments
A program saját adata, beállítása%AppData% alatti saját mappaSpecialFolder.ApplicationData + Path.Combine
Gépszintű gyorsítótár%LocalAppData%SpecialFolder.LocalApplicationData
Ideiglenes fájlA rendszer temp mappájaPath.GetTempPath()
A program mellé csomagolt (csak olvasható) erőforrásA program mappájaAppContext.BaseDirectory (régi .NET Framework: Application.StartupPath)
Form1.csC#
// a program saját adatmappája: létrehozva, ha még nincs
private static string AdatMappa()
{
string ut = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MiniJegyzet");

Directory.CreateDirectory(ut);

return ut;
}

// a program mappája, az erőforrásfájlokhoz (a Program Files alá NE írj!)
string sablon = Path.Combine(AppContext.BaseDirectory, "sablonok", "alap.txt");

Jósolj!

A program a File.WriteAllText("beallitas.txt", ...) hívással ment. A felhasználó az OpenFileDialog segítségével egy másik mappából nyitott meg fájlt, utána mentett. Hová kerül a beallitas.txt?

Jóslat ellenőrzése

Ahhoz a mappához, amely a munkamappa éppen (az OpenFileDialog a munkamappát megváltoztathatta, ha a RestoreDirectory hamis): az a mappa, ahonnan a felhasználó a fájlt megnyitotta. A beállítás „eltűnik” a következő indításkor. Ezért teljes útvonalat (AdatMappa()) használj.

📷 Képernyőkép helye

Mit tegyél ide: a fájlkezelő-labor: a mappa fájlistája (név, méret, dátum), a műveletek gombjai és az állapotsor.Javasolt fájlnév: winforms-10-1-fajlkezelo.webp · Alt szöveg: A fájlkezelő-labor program

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. A hozzáfűzés felülírja a fájlt

File.WriteAllText(naplo, uzenet + Environment.NewLine);   // minden hívás törli az előzőeket
Hiba és javítás

Hozzáfűzéshez File.AppendAllText kell. A WriteAllText mindig felülír.

2. DirectoryNotFoundException a mentéskor

File.WriteAllText(Path.Combine(mappa, "mentesek", "a.txt"), tartalom);   // a mentesek mappa nincs
Hiba és javítás

A File.WriteAllText a fájlt létrehozza, a mappát nem. Előbb Directory.CreateDirectory(Path.GetDirectoryName(ut)) (nem hiba, ha már létezik).

3. A kézi útvonalfűzés hibás útvonalat ad

string ut = mappa + "adat.txt";   // C:\Mappa + adat.txt = C:\Mappaadat.txt
Hiba és javítás

A \ jel hiányzik. Használd a Path.Combine metódust.

4. ArgumentException a fájlnévben lévő karakter miatt

A jegyzet címe Teendő: 1/2, és a fájl mentése hibázik.

Ok és javítás

A : és a / tiltott karakter a fájlnévben. Tisztítsd a nevet (FajlnevTisztit, GetInvalidFileNameChars).

5. A mentés a program mappájába hibázik telepítés után

Fejlesztéskor működik, telepítve UnauthorizedAccessException jön.

Ok és javítás

A Program Files mappába a felhasználó nem írhat. Az adatokat a %AppData% alá mentsd (AdatMappa()).

6. A File.Exists után mégis hiba keletkezik

if (File.Exists(ut)) { string s = File.ReadAllText(ut); }   // két lépés között a fájl eltűnhet vagy zárolt lehet
Hiba és javítás

A vizsgálat és a használat között a fájl változhat. A File.Exists csak jelzés: a műveletet try–catch védje (10.9 lecke).

Mini projekt: fájlkezelő-labor

Készíts kis fájlkezelőt, amely biztonságosan kezeli a fájlokat és mappákat:

  1. Felület: Mappa kiválasztása... (FolderBrowserDialog, 7.8 lecke), lblMappa, lstFajlok (ListView Details nézettel: Név, Méret, Módosítva) vagy DataGridView; gombok: Új mappa, Átnevezés, Másolás ide..., Áthelyezés ide..., Törlés, Mappa mérete; txtKereses (a névrészlet szerinti szűréshez); állapotsor.
  2. Lista: a mappa fájljai és almappái (EnumerateFiles, EnumerateDirectories), a mappák elöl, a méret olvasható alakban (B, KB, MB, 7.8 lecke), a dátum LastWriteTime; a védett fájlok adatai hibát nem okozhatnak (try–catch).
  3. Új mappa: a név bekérése, FajlnevTisztit, létezés-ellenőrzés (YesNo felülírás nincs: a mappa már létezik üzenet), CreateDirectory.
  4. Átnevezés: File.Move (fájl) és Directory.Move (mappa); a név tisztítása; ütközés (már létező név) kezelése; a kiterjesztés megtartása, ha a felhasználó nem ír újat.
  5. Másolás és áthelyezés: cél mappa választása; azonos nevű fájlnál YesNo kérdés (Button2 a Nem); mappa másolása rekurzívan (saját metódus).
  6. Törlés: több kijelölés; megerősítés a darabszámmal; a mappa törlése csak tartalmával együtt, külön erős figyelmeztetéssel (a Windows-gyökér és a felhasználói mappa védett: nem törölhető).
  7. Mappa mérete: a rekurzív MappaMeret kivételkezeléssel; a kihagyott (védett) mappák számát az állapotsor mutatja.
  8. Biztonság: útvonal-bejárás elleni védelem az átnevezésnél (a név nem tartalmazhat \ jelet); nincs törlés a gyökérmappán; minden művelet hibájára érthető üzenet.
  9. Teszteld: ékezetes és szóközös nevek, 1000 fájlos mappa, védett mappa (C:\Windows\System32\config), foglalt fájl törlése, már létező cél, nem létező mappa (időközben törölt), kijelölés nélküli gombok.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A kulcs a Muvelet segédmetódus: minden fájlművelet ezen megy át, és a tipikus kivételeket érthető szöveggé alakítja.

Form1.cs (using System.IO; using System.Linq; using System.Collections.Generic;)C#
private string mappa = "";

private string Meret(long bajt)
{
string[] egyseg = { "B", "KB", "MB", "GB" };
double ertek = bajt;
int i = 0;

while (ertek >= 1024 && i < egyseg.Length - 1) { ertek /= 1024; i++; }

return ertek.ToString(i == 0 ? "0" : "0.#") + " " + egyseg[i];
}

private string FajlnevTisztit(string nev)
{
foreach (char c in Path.GetInvalidFileNameChars()) { nev = nev.Replace(c, '_'); }

nev = nev.Trim().TrimEnd('.');

return nev.Length == 0 ? "nevtelen" : nev;
}

// minden művelet itt megy át: a kivételek érthető szöveggé alakulnak
private bool Muvelet(Action munka, string leiras)
{
try
{
munka();
tsslUzenet.Text = leiras + ": kész.";
return true;
}
catch (UnauthorizedAccessException) { Hiba(leiras, "Nincs jogosultság ehhez a művelethez."); }
catch (DirectoryNotFoundException) { Hiba(leiras, "A mappa időközben megszűnt vagy nem elérhető."); }
catch (FileNotFoundException) { Hiba(leiras, "A fájl időközben megszűnt."); }
catch (PathTooLongException) { Hiba(leiras, "Az útvonal túl hosszú."); }
catch (IOException ex) { Hiba(leiras, "A fájl használatban van, vagy a lemez nem elérhető (" + ex.Message + ")."); }

return false;
}

private void Hiba(string leiras, string uzenet)
{
MessageBox.Show(this, uzenet, leiras, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}

private void Betolt()
{
lvFajlok.Items.Clear();

if (!Directory.Exists(mappa)) { return; }

string szuro = txtKereses.Text.Trim();

try
{
foreach (string d in Directory.EnumerateDirectories(mappa))
{
string nev = Path.GetFileName(d);

if (szuro.Length == 0 || nev.IndexOf(szuro, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
ListViewItem t = new ListViewItem("📁 " + nev);
t.SubItems.Add("");
t.SubItems.Add(Directory.GetLastWriteTime(d).ToString("yyyy. MM. dd. HH:mm"));
t.Tag = d;
lvFajlok.Items.Add(t);
}
}

foreach (string f in Directory.EnumerateFiles(mappa))
{
string nev = Path.GetFileName(f);

if (szuro.Length == 0 || nev.IndexOf(szuro, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
FileInfo fi = new FileInfo(f);
ListViewItem t = new ListViewItem(nev);
t.SubItems.Add(Meret(fi.Length));
t.SubItems.Add(fi.LastWriteTime.ToString("yyyy. MM. dd. HH:mm"));
t.Tag = f;
lvFajlok.Items.Add(t);
}
}
}
catch (UnauthorizedAccessException) { Hiba("Listázás", "Ehhez a mappához nincs jogosultságod."); }
catch (IOException ex) { Hiba("Listázás", ex.Message); }

tsslUzenet.Text = lvFajlok.Items.Count + " elem";
}

private void btnMappa_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog fbd = new FolderBrowserDialog())
{
if (Directory.Exists(mappa)) { fbd.SelectedPath = mappa; }

if (fbd.ShowDialog(this) == DialogResult.OK) { mappa = fbd.SelectedPath; lblMappa.Text = mappa; Betolt(); }
}
}

private void txtKereses_TextChanged(object sender, EventArgs e) { Betolt(); }

private string Kijelolt()
{
return lvFajlok.SelectedItems.Count == 0 ? null : (string)lvFajlok.SelectedItems[0].Tag;
}

private void btnUjMappa_Click(object sender, EventArgs e)
{
if (!Directory.Exists(mappa)) { return; }

string nev = KerdesForm.Kerdez(this, "Az új mappa neve:", "Új mappa"); // saját bekérő ablak: kis Form egy TextBox-szal és OK/Mégse gombbal (7.6–7.7 lecke); Mégse esetén null

if (nev == null) { return; }

string ut = Path.Combine(mappa, FajlnevTisztit(nev));

if (Directory.Exists(ut) || File.Exists(ut)) { Hiba("Új mappa", "Ilyen nevű elem már létezik."); return; }

if (Muvelet(() => Directory.CreateDirectory(ut), "Új mappa")) { Betolt(); }
}

private void btnAtnevez_Click(object sender, EventArgs e)
{
string regi = Kijelolt();

if (regi == null) { return; }

string nev = KerdesForm.Kerdez(this, "Az új név:", Path.GetFileName(regi));

if (nev == null) { return; }

nev = FajlnevTisztit(nev);

if (File.Exists(regi) && Path.GetExtension(nev).Length == 0) { nev += Path.GetExtension(regi); } // a kiterjesztés megmarad

string uj = Path.Combine(Path.GetDirectoryName(regi), nev);

if (File.Exists(uj) || Directory.Exists(uj)) { Hiba("Átnevezés", "Ilyen nevű elem már létezik."); return; }

if (Muvelet(() => { if (File.Exists(regi)) { File.Move(regi, uj); } else { Directory.Move(regi, uj); } }, "Átnevezés")) { Betolt(); }
}

private void MappaMasol(string forras, string cel)
{
Directory.CreateDirectory(cel);

foreach (string f in Directory.EnumerateFiles(forras)) { File.Copy(f, Path.Combine(cel, Path.GetFileName(f)), false); }

foreach (string d in Directory.EnumerateDirectories(forras)) { MappaMasol(d, Path.Combine(cel, Path.GetFileName(d))); }
}

private void btnMasol_Click(object sender, EventArgs e)
{
string forras = Kijelolt();

if (forras == null) { return; }

using (FolderBrowserDialog fbd = new FolderBrowserDialog())
{
if (fbd.ShowDialog(this) != DialogResult.OK) { return; }

string cel = Path.Combine(fbd.SelectedPath, Path.GetFileName(forras));

if (File.Exists(cel) && MessageBox.Show(this, "A célmappában már van ilyen fájl. Felülírod?", "Másolás", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes) { return; }

Muvelet(() => { if (File.Exists(forras)) { File.Copy(forras, cel, true); } else { MappaMasol(forras, cel); } }, "Másolás");
}
}

private bool Vedett(string ut)
{
string teljes = Path.GetFullPath(ut).TrimEnd(Path.DirectorySeparatorChar);
string gyoker = Path.GetPathRoot(teljes).TrimEnd(Path.DirectorySeparatorChar);
string felh = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile).TrimEnd(Path.DirectorySeparatorChar);
string win = Environment.GetFolderPath(Environment.SpecialFolder.Windows).TrimEnd(Path.DirectorySeparatorChar);

return string.Equals(teljes, gyoker, StringComparison.OrdinalIgnoreCase) || string.Equals(teljes, felh, StringComparison.OrdinalIgnoreCase) || teljes.StartsWith(win, StringComparison.OrdinalIgnoreCase);
}

private void btnTorol_Click(object sender, EventArgs e)
{
List<string> utak = lvFajlok.SelectedItems.Cast<ListViewItem>().Select(t => (string)t.Tag).ToList();

if (utak.Count == 0) { return; }

if (utak.Any(Vedett)) { Hiba("Törlés", "Védett mappa nem törölhető."); return; }

bool vanMappa = utak.Any(Directory.Exists);
string kerdes = utak.Count + " elem törlése" + (vanMappa ? " (mappa a tartalmával együtt, véglegesen!)" : "") + ". Biztosan?";

if (MessageBox.Show(this, kerdes, "Törlés", MessageBoxButtons.YesNo, vanMappa ? MessageBoxIcon.Stop : MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes) { return; }

foreach (string ut in utak)
{
Muvelet(() => { if (File.Exists(ut)) { File.Delete(ut); } else if (Directory.Exists(ut)) { Directory.Delete(ut, true); } }, "Törlés");
}

Betolt();
}

private long kihagyott = 0;

private long MappaMeret(string ut)
{
long ossz = 0;

try
{
foreach (string f in Directory.EnumerateFiles(ut)) { ossz += new FileInfo(f).Length; }
foreach (string d in Directory.EnumerateDirectories(ut)) { ossz += MappaMeret(d); }
}
catch (UnauthorizedAccessException) { kihagyott++; }
catch (IOException) { kihagyott++; }

return ossz;
}

private void btnMeret_Click(object sender, EventArgs e)
{
string ut = Kijelolt() != null && Directory.Exists(Kijelolt()) ? Kijelolt() : mappa;

if (!Directory.Exists(ut)) { return; }

kihagyott = 0;
Cursor = Cursors.WaitCursor;

long meret = MappaMeret(ut);

Cursor = Cursors.Default;
tsslUzenet.Text = Path.GetFileName(ut) + ": " + Meret(meret) + (kihagyott > 0 ? " (" + kihagyott + " védett mappa kihagyva)" : "");
}
📷 Képernyőkép helye

Mit tegyél ide: a kész fájlkezelő-labor egy mappa tartalmával és a mérettel az állapotsorban.Javasolt fájlnév: winforms-10-1-kesz.webp · Alt szöveg: A kész fájlkezelő-labor

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. Egyedi fájlnév

Ha a jelentes.txt már létezik, a program a jelentes (2).txt, jelentes (3).txt nevet válassza. Hogyan?

Egy lehetséges megoldás

Ciklus, amely a számot növeli, amíg a név szabad. A Path.GetFileNameWithoutExtension és GetExtension bontja a nevet.

Form1.csC#
private string EgyediUtvonal(string ut){    string mappa = Path.GetDirectoryName(ut);    string nev = Path.GetFileNameWithoutExtension(ut);    string kit = Path.GetExtension(ut);    int i = 2;    while (File.Exists(ut))    {        ut = Path.Combine(mappa, nev + " (" + i + ")" + kit);        i++;    }    return ut;}

2. Fájlok rendezése kiterjesztés szerint

Egy mappa fájljait másold a kiterjesztés nevű almappákba (txt, png…). Mire kell vigyáznod?

Egy lehetséges megoldás

A kiterjesztés nélküli fájlra (egyéb mappa), az azonos nevű ütközésre (EgyediUtvonal), a kis- és nagybetűre (.JPG és .jpg ugyanaz), és a hibás fájlok átugrására.

3. Régi fájlok keresése

Keresd meg a mappában a 1 évnél régebben módosított, 10 MB-nál nagyobb fájlokat. Mit használsz?

Egy lehetséges megoldás

A DirectoryInfo.EnumerateFiles FileInfo objektumokat ad: LINQ Where a LastWriteTime és Length szerint.

Form1.csC#
DirectoryInfo di = new DirectoryInfo(mappa);var regiek = di.EnumerateFiles().Where(f => f.LastWriteTime < DateTime.Now.AddYears(-1) && f.Length > 10 * 1024 * 1024).OrderByDescending(f => f.Length).ToList();

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. Path bontás

Mi a Path.GetFileNameWithoutExtension(@"C:\A\B\jegyzet.v2.txt") értéke?

Megoldás megnyitása

jegyzet.v2: csak az utolsó kiterjesztést (.txt) hagyja el.

2. Melyik metódus?

Melyiket használod: (a) hozzáfűzés a naplóhoz; (b) átnevezés; (c) mappa létrehozása köztes mappákkal?

Megoldás megnyitása

(a) File.AppendAllText; (b) File.Move (Directory.Move); (c) Directory.CreateDirectory.

Közepes szint

3. Kiterjesztés összehasonlítása

Hogyan döntöd el, hogy egy fájl kép-e (.png, .jpg, .jpeg), betűnagyságtól függetlenül?

Megoldás megnyitása

A Path.GetExtension(f).ToLowerInvariant() egy tömb (HashSet) elemeivel összevetve.

Form1.csC#
HashSet<string> kepek = new HashSet<string> { ".png", ".jpg", ".jpeg" };bool kep = kepek.Contains(Path.GetExtension(f).ToLowerInvariant());

4. Útvonal-bejárás védelme

A felhasználó a ..\..\titok.txt nevet adja meg. Hogyan akadályozod meg, hogy kilépjen az adatmappából?

Megoldás megnyitása

A teljes útvonalat (GetFullPath) összeveted az alapmappa teljes útvonalával: StartsWith.

Form1.csC#
string alap = Path.GetFullPath(adatMappa) + Path.DirectorySeparatorChar;string cel = Path.GetFullPath(Path.Combine(adatMappa, nev));if (!cel.StartsWith(alap, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Érvénytelen útvonal."); }

Kihívás szint

5. Duplikált fájlok

Keresd meg egy mappában az azonos tartalmú fájlokat. Hogyan lesz hatékony?

Megoldás megnyitása

Előbb a méret szerint csoportosítasz (az eltérő méretűek nem azonosak), és csak az azonos méretűeknél számolsz tartalmi ujjlenyomatot (hash, például SHA-256). A nagy fájloknál a hash számítása lassú: háttérszálon fusson (11. fejezet).

6. Figyelő a mappán

A program észlelje, ha a mappában új fájl jelenik meg. Mi a megoldás?

Megoldás megnyitása

A FileSystemWatcher osztály (Created, Changed, Deleted, Renamed események). Az események másik szálon érkeznek: a felület frissítéséhez Invoke kell (11. fejezet).

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mi a különbség a WriteAllText és az AppendAllText között?

A WriteAllText felülír, az AppendAllText a végére fűz.

2. Mikor használsz FileInfo-t?

Ha egy fájl több adatát (méret, dátum, név) kérdezed le, vagy objektumként kezeled.

3. Miért Path.Combine?

A megfelelő elválasztót teszi, nem hibázik kézi fűzésnél.

4. Hogyan kezeled a tiltott karaktereket a fájlnévben?

A Path.GetInvalidFileNameChars karaktereit lecseréled.

5. Hová mentsd a program adatait?

A %AppData% alatti saját mappába, nem a program mappájába vagy relatív útvonalra.

6. Miért veszélyes a Directory.Delete(ut, true)?

Mindent töröl, a szemétkosár nélkül: megerősítés és útvonal-ellenőrzés kell.

7. Elég a File.Exists a hibák ellen?

Nem: a vizsgálat és a használat között a fájl változhat; try–catch kell.

Mit tudsz már?

  • Használod a File, FileInfo, Directory osztályok metódusait.
  • Útvonalat építesz és bontasz a Path osztállyal, és tisztítod a fájlneveket.
  • Mappát hozol létre, listázol, másolsz és törölsz, védett mappák kezelésével.
  • Tudod, hová írhat a program, és elkerülöd a relatív útvonal csapdáit.
  • A műveleteket kivételkezeléssel és érthető üzenetekkel védd.

Következő lecke: StreamReader, StreamWriter és using

Fájlok olvasása és írása adatfolyamként: soronként, nagy fájlokkal, a using és az FileStream szerepével.