JavaRush /Курси /C# SELF /Перехоплення файлових винятків (

Перехоплення файлових винятків ( try-catch)

C# SELF
Рівень 38 , Лекція 1
Відкрита

1. Вступ

Операції з файлами майже завжди повʼязані з зовнішніми щодо програми обставинами: файл можуть видалити, перемістити, його може заблокувати інша програма, раптом у користувача закінчилися права або вільне місце на диску. Усе це може призвести до винятків — спеціальних ситуацій, коли виконання програми переривається і «викидається» обʼєкт винятку (Exception). Якщо не перехопити й не обробити такий виняток, ваш застосунок завершиться з помилкою і покаже користувачеві моторошний червоний текст у консолі (або, що ще гірше, — мовчки впаде).

У C# для обробки таких ситуацій використовується блок try-catch (або, простими словами, «пастка помилок»). Він дозволяє не лише «спіймати» помилку, а й спокійно вирішити, що робити далі: показати дружнє повідомлення, запропонувати обрати інший файл, записати помилку в журнал або просто повторити спробу.

У реальних застосунках

Якщо ви пишете консольний калькулятор, то, мабуть, можна обійтися і без перехоплення помилок під час роботи з файлами. А от якщо у вас застосунок для обробки документів на підприємстві або гра, що зберігає прогрес, — не обробляти такі помилки все одно що ходити канатом без страховки: рано чи пізно щось та й станеться.

2. Згадуємо роботу з винятками

Базовий синтаксис try-catch

Перш ніж перейти до конкретних ситуацій із файлами, нагадаємо базовий синтаксис конструкції try-catch (яку вже розглядали в лекціях про винятки):


try
{
    // Тут код, який може "викинути" виняток
}
catch (Exception ex)
{
    // Тут ми ловимо всі можливі винятки... Але краще так не робити!
    Console.WriteLine("Щось пішло не так: " + ex.Message);
}

У блоці try ми розміщуємо потенційно небезпечний код, а в catch — те, що робити, якщо тут щось пішло не так.

Жарт програміста: «try-catch — це цифровий аналог шапки-невидимки для помилок. Помилка є, але ви її не бачите!»

Файлові винятки

Коли ви працюєте із файлами у .NET, найчастіше стикаєтеся з винятками — скажімо, під час створення потоку, читання або запису. Ось кілька типових сценаріїв і повʼязаних із ними винятків:

  • Файл не знайдено → FileNotFoundException
  • Немає доступу до файлу/теки → UnauthorizedAccessException
  • Проблеми зі шляхом (наприклад, неприпустимі символи, надто довгий шлях) → PathTooLongException, ArgumentException
  • Файл уже використовується іншим процесом → IOException
  • Немає вільного місця на диску → IOException

Найчастіше з файловими операціями повʼязані саме похідні від IOException. Усі вони успадковані від базового типу System.Exception.

3. Практика: перехоплюємо й обробляємо помилки під час роботи з файлами

Розгляньмо типові випадки на прикладах нашого «застосунку, що еволюціонує»: припустімо, ми намагаємося зчитати привітання з файлу, обробити його й вивести користувачеві. Додамо сюди перевірку на помилки за допомогою try-catch.

Приклад 1: Обробка «файл не знайдено»


string filePath = "hello.txt";

try
{
    string greeting = File.ReadAllText(filePath);
    Console.WriteLine("Вміст файлу: " + greeting);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("Файл не знайдено! Перевірте, що файл " + filePath + " існує.");
    // Можемо додатково вивести подробиці
    Console.WriteLine("Технічні деталі: " + ex.Message);
}

Якщо файл "hello.txt" відсутній, програма не завершиться з помилкою, а виведе дружнє повідомлення. Так ми робимо застосунок стійким до типових помилок користувача.

Приклад 2: Порушення прав доступу

Візьмімо складнішу ситуацію: файл є, але в нас немає прав на його читання (наприклад, хтось навмисне дав права лише на запис або файл лежить у захищеній теці).


try
{
    string secret = File.ReadAllText("C:\\Windows\\System32\\config.txt");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Немає доступу до файлу! Спробуйте запустити застосунок від імені адміністратора.");
    Console.WriteLine("Технічна причина: " + ex.Message);
}

Приклад 3: Загальний IOException

Деякі помилки можуть бути повʼязані з конфліктом блокувань (наприклад, інший процес утримує файл відкритим), із нестачею місця на диску чи проблемами з обладнанням:


try
{
    File.WriteAllText("important.txt", "Важлива інформація!");
}
catch (IOException ex)
{
    Console.WriteLine("Помилка під час роботи з файлом: найімовірніше, його використовує інший застосунок або на диску недостатньо місця.");
    Console.WriteLine("Технічна причина: " + ex.Message);
}

4. Перехоплення кількох винятків: вибіркова обробка

Іноді потрібно по-різному реагувати на різні типи винятків. У .NET дозволяється вказувати одразу кілька блоків catch — від більш конкретних до загальніших (інакше компілятор влаштує вашому коду гучний італійський страйк).


try
{
    string content = File.ReadAllText("file.txt");
    Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine("Файл не знайдено.");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine("Немає доступу до файлу!");
}
catch (IOException ex)
{
    Console.WriteLine("Інша помилка введення-виведення: " + ex.Message);
}

Важливо: якщо поставити catch (Exception ex) першим, решта блоків не матимуть сенсу, адже базовий тип перехопить усе!

5. Вкладені try-catch і повторні спроби

Іноді ви хочете не лише обробити помилку, а й дати користувачеві шанс «виправитися» — наприклад, запропонувати ввести шлях до наявного файлу:


string filePath;
string content;
int attempts = 0;
const int maxAttempts = 3;

do
{
    Console.Write("Введіть шлях до файлу: ");
    filePath = Console.ReadLine();

    try
    {
        content = File.ReadAllText(filePath);
        Console.WriteLine("Вміст файлу:\n" + content);
        break;
    }
    catch (FileNotFoundException)
    {
        Console.WriteLine("Файл не знайдено! Спробуйте ще раз.");
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("Немає доступу до файлу! Спробуйте інший файл.");
    }
    attempts++;
}
while (attempts < maxAttempts);

if (attempts == maxAttempts)
    Console.WriteLine("Надто багато невдалих спроб.");

Цей код — маленький «інтерфейс дружби» для користувача, який забув, куди зберіг файл.

6. Типові помилки й особливості: від лінощів до усвідомленості

Дуже поширена помилка новачків (і навіть бувалих розробників) — ловити взагалі всі винятки підряд, писати просто catch (Exception) і виводити «Сталася помилка!», не замислюючись про причини. Такий підхід поганий із кількох причин. По‑перше, він приховує реальні недоліки в бізнес‑логіці застосунку. По‑друге, інші, не повʼязані з файлами помилки (наприклад, друкарські помилки в коді або помилка в математиці), можуть бути випадково проковтнуті, і пошук справжньої причини затягнеться надовго.

Набагато краще — явно перехоплювати лише ті помилки, які ви здатні осмислено обробити. Якщо незрозуміло, яка саме помилка сталася, краще дати їй «впасти» — тобто взагалі не ловити: нехай застосунок впаде, зате ви побачите стек викликів і зрозумієте, що треба полагодити.

Особливість: Деякі винятки можуть мати вкладені причини (InnerException). Їх зручно аналізувати для детальнішої діагностики, особливо коли ведете журнали помилок (логи).

Ще один нюанс — якщо після catch-блоку життя застосунку неможливе (наприклад, якщо не вдалося відкрити головний файл налаштувань), можна завершити виконання через return або навіть кинути виняток знову (throw;), щоб не плодити «зомбі‑застосунки» з половиною функцій.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ