Модул 7

Работа с файлове и директории (File I/O)

Как програмата запазва данни, които оцеляват след затварянето ѝ — записване, добавяне и четене на текстови файлове с класа File, проверки за съществуване и работа с директории.

Теория

Всичко в променливите живее само докато програмата работи. За да запазим данни трайно (резултати, настройки, логове), ги записваме във файлове. В C# за това отговаря именното пространство System.IO с класовете File и Directory.

Класът File предлага "всичко-в-един-ред" методи: WriteAllText записва (и презаписва!) файл, AppendAllText добавя в края, ReadAllText чете целия файл като един низ, а ReadAllLines — като масив от редове, което е удобно за обработка ред по ред.

Пътищата биват относителни ("data.txt" — търси се в папката на програмата) и абсолютни (@"C:\MyFolder\data.txt"). Знакът @ пред низа го прави дословен (verbatim) — обратните наклонени черти не се тълкуват като специални символи и пътят се пише естествено.

Файловите операции са "опасни": файлът може да не съществува, да е заключен, да нямаме права. Затова винаги проверяваме с File.Exists() преди четене, а в по-сериозен код ограждаме операциите с try-catch.

За работа ред по ред има удобна двойка: File.WriteAllLines(path, масив) записва всеки елемент на нов ред, а File.ReadAllLines(path) връща string[] — и оттам нататък важи всичко за масивите от Модул 4: обхождане, парсване, броене. Това е най-естественият формат за прости данни.

Пътищата е най-добре да се сглобяват с Path.Combine("папка", "файл.txt") — той сам слага правилните наклонени черти. А програмата по подразбиране работи в своята изпълнима папка (bin/Debug…), не в папката на проекта — затова "изчезналите" файлове обикновено са просто там.

Златни правила

Освобождаване на ресурси

Когато четете или записвате във файл, файлът се "заключва" от вашата програма. Методите на File (ReadAllText и др.) сами отварят и затварят файла — затова са най-безопасният избор за начинаещи.

Пътища до файлове

Правете разлика между относителен път (напр. "data.txt" — търси файла в папката на програмата) и абсолютен път (напр. "C:\MyFolder\data.txt"). При пътища с наклонени черти добавяйте @ пред стринга (напр. @"C:\Folder\file.txt"), за да избегнете грешки.

Проверка преди действие

Винаги проверявайте дали даден файл съществува (File.Exists()), преди да се опитате да го прочетете — иначе рискувате срив на програмата с FileNotFoundException.

WriteAllText трие!

WriteAllText презаписва файла изцяло — старото съдържание изчезва безвъзвратно. Ако искате да добавяте, използвайте AppendAllText.

Бърз справочник

Класове File и Directory

МетодКакво прави?Пример
File.ReadAllText()Чете целия текст от файл и го връща като един string.string text = File.ReadAllText("test.txt");
File.ReadAllLines()Чете файла като масив от редове (string[]).string[] lines = File.ReadAllLines("test.txt");
File.WriteAllText()Създава файл (или го презаписва) и записва текст вътре.File.WriteAllText("test.txt", "Здравей!");
File.AppendAllText()Добавя текст в края на съществуващ файл, без да трие старото съдържание.File.AppendAllText("test.txt", " Нов ред.");
File.Exists()Проверява дали файлът съществува (връща bool).bool exists = File.Exists("test.txt");
File.Delete()Изтрива файл (не гърми, ако липсва).File.Delete("old.txt");
Directory.CreateDirectory()Създава нова папка на посочения път.Directory.CreateDirectory(@"C:\NewFolder");
Directory.Exists()Проверява дали папката съществува.if (Directory.Exists("data")) { }

Код за анализ и пренаписване

Записване, добавяне и четене с проверка

Program.cs
using System;
using System.IO;

namespace ModuleSeven
{
    class Program
    {
        static void Main(string[] args)
        {
            string filePath = "students_list.txt";

            // 1. Записване във файл (презаписва, ако вече съществува)
            File.WriteAllText(filePath, "1. Иван Иванов\n");

            // 2. Добавяне към съществуващ файл
            File.AppendAllText(filePath, "2. Мария Петрова\n");

            Console.WriteLine("Данните са записани успешно!");

            // 3. Четене от файл с проверка
            if (File.Exists(filePath))
            {
                Console.WriteLine("\n--- Съдържание на файла ---");
                string content = File.ReadAllText(filePath);
                Console.WriteLine(content);
            }
            else
            {
                Console.WriteLine("Грешка: Файлът не е намерен!");
            }
        }
    }
}

Какво се случва тук?

  • using System.IO; най-отгоре е задължителен — там живеят File и Directory.
  • Пътят "students_list.txt" е относителен — файлът се създава в папката, от която се изпълнява програмата (обикновено bin/Debug).
  • WriteAllText създава файла, а ако вече съществува — го изтрива и записва наново. Затова е първата операция.
  • AppendAllText добавя втория ред в края, без да пипа първия. \n в края на низовете осигурява нов ред.
  • Преди четенето проверяваме с File.Exists(filePath) — добрата практика, която предпазва от FileNotFoundException. В този пример файлът гарантирано съществува (току-що го създадохме), но навикът е важен.
  • ReadAllText връща цялото съдържание като един низ, който отпечатваме наведнъж.

Среден успех от файл с ReadAllLines

Program.cs
using System;
using System.IO;

namespace ModuleSevenExtra
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = "grades.txt";

            // Подготвяме тестов файл: всяка оценка на отделен ред
            string[] lines = { "5", "6", "4", "5", "6" };
            File.WriteAllLines(path, lines);

            // Четене с проверка
            if (!File.Exists(path))
            {
                Console.WriteLine("Файлът липсва!");
                return; // ранен изход - няма какво да четем
            }

            string[] read = File.ReadAllLines(path);

            double sum = 0;
            foreach (string line in read)
            {
                sum += double.Parse(line);
            }
            double average = sum / read.Length;

            Console.WriteLine($"Прочетени {read.Length} оценки.");
            Console.WriteLine($"Среден успех: {average:F2}");

            // Записваме резултата в отчет (с добавяне, не презапис)
            File.AppendAllText("report.txt",
                $"{DateTime.Now}: среден успех {average:F2}\n");
        }
    }
}

Какво се случва тук?

  • WriteAllLines приема масив и записва всеки елемент на отделен ред — без ръчни \n. Огледалният ReadAllLines връща редовете обратно като string[].
  • Проверката е обърната: if (!File.Exists(path)) return; — шаблонът "ранен изход" пази основната логика от влагане в голям if.
  • Редовете са текст, затова double.Parse(line) ги превръща в числа — комбинация от уменията от Модул 1 и Модул 4.
  • sum / read.Length тук е безопасно дробно делене, защото sum е double — стига едната страна да е дробна, резултатът е дробен.
  • Отчетът се записва с AppendAllText, не с WriteAllText — всяко пускане добавя нов ред към историята, вместо да изтрие старата. За логове и отчети — винаги Append.

Внимавай! Чести грешки

Грешките, които почти всеки прави в тази тема — виж разликата между грешния и правилния код.

WriteAllText в цикъл

Грешно

foreach (string name in names)
{
    File.WriteAllText("list.txt", name + "\n");
}
// във файла остава САМО последното име

Правилно

foreach (string name in names)
{
    File.AppendAllText("list.txt", name + "\n");
}
// или още по-добре: File.WriteAllLines("list.txt", names);

WriteAllText презаписва файла при всяко извикване — всяка итерация трие предишната. За натрупване ползвайте AppendAllText, а за готова колекция — WriteAllLines с един ред код.

Четене без проверка за съществуване

Грешно

string text = File.ReadAllText("data.txt");
// FileNotFoundException, ако файлът липсва

Правилно

if (File.Exists("data.txt"))
{
    string text = File.ReadAllText("data.txt");
}
else
{
    Console.WriteLine("Файлът не е намерен!");
}

Файлът може да липсва по хиляда причини (първо пускане, изтрит, друг компютър). File.Exists струва един ред и спасява програмата от срив.

Обикновен низ за път с обратни черти

Грешно

string path = "C:\new\table.txt";
// \n и \t се тълкуват като нов ред и табулация!

Правилно

string path = @"C:\new\table.txt";
// @ = дословен низ, чертите са си черти

В обикновен низ \n е нов ред, \t — табулация: пътят се чупи тихо и файлът "не се намира". Знакът @ пред кавичките изключва специалните последователности — правило за всички Windows пътища.

Задачи за самостоятелна работа

Опитай първо сам — подсказката е там само ако наистина закъсаш.

Задача 1: Логване на грешки

Напишете програма, която симулира грешка и записва часа на грешката и съобщение в текстов файл errors.log. Използвайте DateTime.Now, за да вземете точния час.

Задача 2: Четене на масив от файл

Създайте текстов файл ръчно с няколко числа на отделни редове. Напишете програма, която използва File.ReadAllLines(), за да ги прочете, преобразува ги в int и отпечатва тяхната сума.

Задача 3: Бележник със задачи

Направете конзолен "бележник": команда "добави <текст>" добавя ред във файла todo.txt, команда "покажи" отпечатва всички задачи с номера, команда "край" спира програмата.

Мини-тест за проверка

Отговори си наум (или на глас!) и чак тогава разкрий отговора.

1. Кой namespace (именно пространство) трябва да включите най-отгоре, за да работите с класовете File и Directory?

2. Каква е разликата между WriteAllText и AppendAllText?

3. Защо е важно да използваме метода File.Exists()?

4. Какво връща File.ReadAllLines() и защо е удобно?

Речник на термините

Виж пълния речник на курса
Файл
Именувана порция данни на диска, която оцелява след затварянето на програмата.
Директория (папка)
Контейнер за файлове и други директории. В C# се управлява с класа Directory.
Относителен път
Път спрямо работната папка на програмата: "data.txt" се търси в bin/Debug на проекта.
Абсолютен път
Пълен път от корена на диска: @"C:\MyFolder\data.txt" — еднозначен, но непреносим между компютри.
Дословен низ (@)
Низ с @ пред кавичките, в който \ не е специален символ — задължителен за Windows пътища.
Лог
Файл-дневник, в който програмата записва събития и грешки с дата и час — винаги с AppendAllText.
System.IO
Именното пространство с класовете за вход/изход: File, Directory, Path.

Провери знанията

Бърз тест с избор от отговори върху термините на модула — с точки и моментална обратна връзка.

Провери знанията си

7 въпроса върху термините от модула. Показваме определение — ти избираш правилния термин.

Препоръки за този модул

  • Докато се учите, работете с относителни пътища — файлът се появява в папката bin/Debug на проекта; намерете го и го отворете, за да видите резултата.
  • Тествайте "лошия" сценарий нарочно: изтрийте файла и пуснете програмата — добре написаният код трябва да съобщи грешка, а не да гръмне.
  • Помнете \n за нов ред при записване — иначе всичко се слива на един ред.
  • Логването във файл (както в Задача 1) е реална практика — всяко сериозно приложение пише лог.

Начален курс по C# — учебен наръчник и бърз справочник за студенти.

Натисни Ctrl K или / отвсякъде, за да търсиш.