1. Вступ
Коли ви працюєте з файлами через FileStream або інші потокові класи, дуже важливо заздалегідь розуміти, що саме станеться з файлом під час спроби його відкрити. Часто трапляється, що файл уже існує, і ви хочете або додати нові дані, не втративши старі, або повністю перезаписати файл. Водночас бувають ситуації, коли файл ще не створено, а ви хочете його прочитати, не ризикуючи випадково створити порожній файл.
Наприклад, якщо ви пишете блокнот для щоденних нотаток, ваше завдання — додавати нові записи в кінець наявного файлу, зберігаючи все, що було написано раніше. Це означає, що потрібно відкрити файл у режимі додавання. В іншому випадку — скажімо, у вас є лог‑файл, який щоразу має створюватися заново під час запуску застосунку, щоб не змішувати старі дані. Тут слід відкрити файл так, щоб під час відкриття він очищався або створювався заново, якщо не існує. А якщо ваше завдання — просто прочитати наявний файл, ви, найімовірніше, захочете переконатися, що не створите новий файл, якщо його немає, інакше можете отримати порожній файл там, де його не повинно бути.
Усі ці ситуації — додавання, перезапис, читання без створення — реалізуються у C# через спеціальні режими відкриття файлів, які задаються перерахуванням System.IO.FileMode. Кожен режим керує поведінкою під час відкриття або створення файлу, допомагаючи вам точно контролювати, що відбувається з файлом під час взаємодії з ним.
2. Перерахування FileMode — усі варіанти простими словами
Ось основні значення перерахування FileMode:
| Значення | Опис | Поведінка, якщо файл існує | Поведінка, якщо файл відсутній |
|---|---|---|---|
|
Створює новий файл. Якщо файл уже існує — викидає виняток | Помилка | Гаразд, файл створено |
|
Створює новий файл якщо його немає, або перезаписує наявний | Перезаписує | Гаразд, створює |
|
Відкриває наявний файл. Якщо файлу немає — виняток | Гаразд, відкриває | Помилка |
|
Відкриває, якщо існує; якщо ні — створює новий | Гаразд, відкриває | Гаразд, створює |
|
Відкриває наявний файл і скидає його довжину до нуля (вміст видаляється) | Гаразд, очищає файл | Помилка |
|
Відкриває файл для дописування в кінець, якщо немає — створює новий | Гаразд, додає в кінець | Гаразд, створює |
Ілюстрація: Сценарії поведінки
3. Приклади відкриття файлів у різних режимах
Спробуймо все це на практиці в нашому міні‑застосунку, який ми розвиваємо протягом курсу. Припустімо, у нас є текстовий файл, до якого ми записуємо привітання та дату звернення. Розгляньмо три типові сценарії:
- А. Створюємо новий файл (CreateNew)
- Б. Перезаписуємо файл (Create)
- В. Додаємо рядки в кінець файлу (Append)
Створення нового файлу (FileMode.CreateNew)
Якщо файл уже існує, цей режим гарантує, що створити його заново не вийде: програма викине виняток IOException. Це доречно, коли не можна втрачати дані або випадково стирати чужі файли.
using System;
using System.IO;
string filePath = "greeting.txt";
// Спробуємо створити новий файл — якщо він уже є, буде виняток!
try
{
var stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("Привіт! Це перший запис.");
writer.WriteLine($"Дата: {DateTime.Now}");
Console.WriteLine("Файл успішно створено.");
writer.Close();
}
catch (IOException)
{
Console.WriteLine($"Файл '{filePath}' вже існує! Не будемо його перезаписувати.");
}
Зверніть увагу: якщо ви запускаєте цей код удруге — спрацює блок catch, дані не буде стерто.
Перезапис файлу (FileMode.Create)
Цей режим діє рішуче: якщо файл уже є, його вміст повністю видаляється. Якщо немає — створюється новий. Зручно, коли щоразу треба починати «з чистого аркуша». Наприклад, перезаписувати звіт.
using System;
using System.IO;
string filePath = "greeting.txt";
// Створення або повний перезапис файлу
var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine("Привіт! Я повністю оновив файл :)");
writer.WriteLine($"Оновлено: {DateTime.Now}");
Console.WriteLine("Файл створено або перезаписано.");
writer.Close();
Запустіть цей код кілька разів — у файлі завжди будуть лише останні додані рядки. Усе старе зникає без сліду.
Дописування в кінець файлу (FileMode.Append)
Зручний режим для логів, журналів подій, обліку відвідувань тощо. Якщо файлу немає — він створюється. Якщо є — дані додаються в кінець.
using System;
using System.IO;
string filePath = "greeting.txt";
// Додавання нового рядка в кінець файлу
var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write);
var writer = new StreamWriter(stream);
writer.WriteLine($"Ще одне привітання! Дата: {DateTime.Now}");
Console.WriteLine("Додали новий рядок у кінець файлу.");
writer.Close();
Спробуйте запускати цей код багато разів поспіль. Файл зростатиме, кожен новий запис буде в самому кінці.
4. Як обрати режим відкриття файлу? Практичні поради
Сценарії з реального життя:
- Логування: майже завжди використовуйте Append. Перезапис журналів — болісно для адміністратора і подарунок зловмиснику.
- Експорт звіту: частіше використовуйте Create, щоб не захаращувати каталог старими версіями.
- Вивантаження даних, втрату яких не можна допустити: тут підходить CreateNew. Якщо файл є — помилка, нічого не чіпаємо.
- Читання файлу: режим створення взагалі не потрібен, використовуйте FileMode.Open — якщо файлу немає, це сигналізує про проблему.
Під час написання коду на C# часто застосовують статичні методи класу File, які всередині самі добирають відповідний режим. Наприклад, File.AppendAllText завжди працює через Append, а File.WriteAllText — через Create.
5. Файлові режими і проблеми при роботі
Кілька підступних моментів:
Якщо ви відкриваєте файл у режимі FileMode.Create або FileMode.Truncate, увесь вміст буде знищено. Будьте обережні — випадкової втрати даних зазнавали не лише студенти, а й досвідчені програмісти.
Якщо намагаєтеся відкрити неіснуючий файл за допомогою FileMode.Open або FileMode.Truncate, отримаєте FileNotFoundException. Не забувайте перевіряти, чи файл справді існує, за допомогою File.Exists(path).
З режимом Append можна лише записувати. Якщо вам потрібно і читати, і писати, розгляньте інші режими (OpenOrCreate + відповідний FileAccess).
Приклад: Читання й запис
Іноді потрібно і читати, і писати у файл (наприклад, ви відкрили банківський рахунок — хочете і внести гроші, і перевірити баланс). Використовуйте FileMode.OpenOrCreate:
using System.IO;
string filePath = "balance.txt";
// Відкриття файлу для читання і запису; якщо немає — створити
var stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
// Тут можна і читати, і писати
6. Корисні нюанси
Режими відкриття файлу
Зведена таблиця режимів відкриття
| FileMode | Файл існує | Файл не існує | Доступ до даних | Видаляє старий вміст? |
|---|---|---|---|---|
|
Виняток | Створює новий | Запис | Н/Д |
|
Перезаписує | Створює новий | Запис | Так |
|
Відкриває | Виняток | Читання/запис | Ні |
|
Відкриває | Створює новий | Читання/запис | Ні |
|
Очищає | Виняток | Запис | Так |
|
Відкриває для запису в кінець | Створює новий | Лише запис | Ні (дописує) |
Порівняння з високорівневими методами класу File
- File.WriteAllText(path, text) → всередині використовує FileMode.Create, тобто старий файл перезапишеться.
- File.AppendAllText(path, text) → використовується режим Append, усе допишеться в кінець.
- File.ReadAllText(path) → відкриває файл на читання (FileMode.Open).
Іноді цих методів достатньо, але робота з FileStream та явним зазначенням режиму дає повний контроль над тим, що відбувається.
Особливості для різних операційних систем
Часто розробники не враховують, що у Windows файл, відкритий іншим процесом для запису, може бути недоступний для повторного відкриття — спроба доступу призведе до помилки. Тому дуже важливо правильно використовувати параметр FileShare, щоб керувати спільним доступом до файлу (про це ми детальніше поговоримо в одній із наступних лекцій). У різних операційних системах механізми блокувань і дозволів можуть відрізнятися, однак сама концепція режимів відкриття файлів залишається універсальною.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ