1. Клас File: простота й ефективність
Клас System.IO.File втілює концепцію «одна операція — один метод». Він пропонує набір високорівневих методів, які інкапсулюють складну логіку взаємодії з операційною системою, дозволяючи вам зосередитися на своєму завданні.
Що саме вміє клас File?
- Запис тексту або довільних бінарних даних у файл.
- Читання вмісту файлу повністю або по рядках (для текстових файлів).
- Копіювання, видалення й перейменування наявних файлів.
- Перевірка існування файлу за заданим шляхом.
- Робота з атрибутами файлу (наприклад, дата створення, розмір — через супровідні класи).
Чому це зручно?
Усі методи File — статичні. Це означає, що для їх використання не потрібно створювати об’єкт: достатньо просто викликати потрібний метод, наприклад:
File.WriteAllText("data.txt", "Привіт, файл!");
Такий стиль дуже подібний до роботи з класом Console: усе просто й зрозуміло, не потрібно нічого створювати й не доведеться морочитися з потоками.
Якщо ви шукаєте універсальний і зручний інструмент для роботи з файлами, клас System.IO.File — саме такий. Він пропонує багато методів для читання, запису, копіювання, видалення та інших базових операцій з файлами — мов швейцарський ніж для файлової системи.
Як використовувати File?
Усі методи класу File статичні, тобто не потрібно створювати об’єкт, щоб їх використовувати — просто пишете File.Щось(). Це дуже схоже на виклики на кшталт Console.WriteLine(), тільки тепер для файлів.
string text = "Привіт, світ!";
string path = "hello.txt";
// Запис тексту у файл (якщо файл існує — перезапише!)
File.WriteAllText(path, text);
// Читання всього файлу в один рядок
string readText = File.ReadAllText(path);
Console.WriteLine("Вміст файлу: " + readText);
Лайфхак: Якщо ви десять разів поспіль викликаєте ReadAllText і WriteAllText, швидше за все, ви робите щось не так — ці методи зручні для простих сценаріїв і невеликих файлів.
Часто використовувані методи класу File
| Метод | Опис |
|---|---|
|
Перевіряє, чи існує файл за вказаним шляхом. |
|
Видаляє файл. |
|
Копіює файл. |
|
Переміщує (або перейменовує) файл. |
|
Читає весь файл як один рядок. |
|
Записує рядок у файл (перезаписує, якщо він є). |
|
Читає всі рядки файлу в масив рядків. |
|
Записує масив рядків у файл (кожен рядок — окремий). |
|
Читає всі байти файлу в масив байтів. |
|
Записує масив байтів у файл. |
2. Запис і читання текстових файлів
Розгляньмо найчастіше використовувані методи класу File із практичними прикладами. Для роботи з ними завжди потрібна директива using System.IO; на початку вашого файла з кодом.
using System;
using System.IO; // Обов’язково для роботи з класом File
using System.Linq; // Може знадобитися для ReadLines
Читання файлу повністю
Це одна з найчастіших задач: зберегти текстові дані у файл або завантажити їх назад.
Запис одного рядка (або всього тексту) у файл: File.WriteAllText() Цей метод створює новий файл за вказаним шляхом. Якщо файл із таким іменем уже існує, його вміст буде повністю перезаписано новим текстом.
string myFilePath = "notes.txt";
string textContent = "Сьогодні чудовий день для вивчення C#.\nНе забудь зробити домашнє завдання.";
// Записуємо весь текст у файл
File.WriteAllText(myFilePath, textContent);
Console.WriteLine($"Файл '{myFilePath}' успішно створено/перезаписано.");
Читання файлу повністю в один рядок: File.ReadAllText() Цей метод зчитує весь вміст текстового файлу від початку до кінця й повертає його у вигляді одного великого рядка.
string myFilePath = "notes.txt";
// Важливо: завжди перевіряйте існування файлу перед читанням, щоб уникнути помилок!
if (File.Exists(myFilePath))
{
string content = File.ReadAllText(myFilePath);
Console.WriteLine($"Вміст файлу '{myFilePath}':\n{content}");
}
else
{
Console.WriteLine($"Помилка: Файл '{myFilePath}' не знайдено для читання.");
}
Важливе зауваження: Методи File.WriteAllText() і File.ReadAllText() ідеально підходять для невеликих і середніх файлів (наприклад, текстові нотатки, конфігураційні файли, короткі журнали подій). Але якщо файл дуже великий (скажімо, десятки або сотні мегабайт, а тим більше гігабайти), завантаження всього його вмісту в один рядок в оперативну памʼять може спожити багато памʼяті і навіть призвести до OutOfMemoryException. Для таких випадків існують ефективніші підходи (про них — далі та в наступних лекціях).
3. Читання файлу по рядках
Коли ви працюєте з великими текстовими файлами, такими як обʼємні CSV-звіти, довгі файли журналів або текстові бази даних, читати їх повністю в памʼять (тобто завантажити весь файл одразу з диска в оперативну памʼять компʼютера) стає неефективно або навіть неможливо. Тут виручає File.ReadLines().
File.ReadLines(path) — цей метод не завантажує весь файл одразу. Замість цього він повертає спеціальну колекцію (IEnumerable<string>), яка дозволяє читати файл рядок за рядком у міру потреби. Це називається «ліниве читання» або «потокове читання».
string logFilePath = "application.log";
string text = """
INFO: Застосунок запущено.
WARN: Виявлено проблему в модулі X.
ERROR: Критична помилка мережі.
INFO: Застосунок зупинено.
""";
// Припустімо, у нас є лог-файл із багатьма рядками.
File.WriteAllText(logFilePath, text);
// Кожна ітерація циклу зчитує й обробляє лише один рядок
foreach (string line in File.ReadLines(logFilePath))
Console.WriteLine($"- {line}");
Перевага: Файл читається й обробляється частинами, що суттєво знижує споживання памʼяті під час роботи з великими файлами.
Різниця між File.ReadLines() і File.ReadAllLines():
- File.ReadAllLines(path): Завантажує усі рядки файлу одразу в оперативну памʼять і повертає їх у вигляді масиву string[]. Після виклику цього методу весь файл уже в памʼяті. Це зручно, якщо вам потрібен повний доступ до всіх рядків одночасно (наприклад, для сортування чи пошуку).
- File.ReadLines(path): Повертає IEnumerable<string>, який дозволяє перебирати рядки файлу по одному. Файл читається тільки тоді, коли ви запитуєте наступний рядок (наприклад, у циклі foreach). Це ефективніше для дуже великих файлів, коли ви не можете або не хочете завантажувати все в памʼять.
4. Запис масиву рядків
Іноді у вас є список рядків (наприклад, із певної колекції), і ви хочете записати кожен рядок як окремий запис у файл.
File.WriteAllLines(path, contents)
Цей метод приймає масив рядків (або будь-яку колекцію рядків) і записує кожен рядок як окремий рядок у файл. Як і WriteAllText(), він перезаписує файл, якщо той уже існує.
string membersFilePath = "participants.txt";
string[] names = { "Іван Петренко", "Марія Сидоренко", "Сергій Козак", "Анна Ковальчук" };
// Кожен рядок масиву буде записано як окремий рядок у файлі
File.WriteAllLines(membersFilePath, names);
// Перевірмо вміст
Console.WriteLine($"Вміст '{membersFilePath}':");
foreach (string line in File.ReadLines(membersFilePath))
Console.WriteLine($"- {line}");
5. Робота з бінарними файлами
Клас File не обмежується лише текстовими даними. Він також пропонує зручні методи для читання й запису бінарних даних, тобто «сирих» байтів. Це корисно, наприклад, під час збереження налаштувань у спеціальному форматі, роботи із зображеннями, аудіо чи будь-якими даними, які не є звичайним текстом.
Запис масиву байтів: File.WriteAllBytes() Створює або перезаписує файл, записуючи в нього весь переданий масив байтів.
string binaryDataPath = "settings.bin";
// Приклад бінарних даних: зображення, відео, будь-які дані
byte[] settingsData = { 0x01, 0x0A, 0x2C, 0xFF, 0x00, 0x3F }; // Деякі «сирі» налаштування
File.WriteAllBytes(binaryDataPath, settingsData);
Console.WriteLine($"Бінарні дані записані у файл '{binaryDataPath}'.");
Важливо: якщо ви спробуєте відкрити 'settings.bin' у Блокноті, побачите нерозбірливі символи («кракозябри»), адже ці байти не призначені для інтерпретації як текст.
Читання масиву байтів: File.ReadAllBytes() Зчитує весь вміст бінарного файлу в масив байтів.
if (File.Exists(binaryDataPath))
{
byte[] readSettings = File.ReadAllBytes(binaryDataPath);
Console.WriteLine($"Прочитано {readSettings.Length} байт з файлу '{binaryDataPath}'.");
Console.Write("Прочитані байти (у шістнадцятковому вигляді): ");
foreach (byte b in readSettings)
Console.Write($"{b:X2} "); // :X2 форматує байт як двозначне шістнадцяткове число
Console.WriteLine();
}
else
{
Console.WriteLine($"Помилка: Бінарний файл '{binaryDataPath}' не знайдено.");
}
Ці методи дуже зручні для збереження невеликих порцій бінарних даних, наприклад, для кешування маленьких зображень чи серіалізованих об’єктів.
6. Керування файлами на диску
Клас File також містить методи для керування файлами на диску.
Перевірка існування файлу: File.Exists(path) Повертає true, якщо файл за вказаним шляхом існує, і false — в іншому випадку. Рекомендовано використовувати цей метод перед спробами читання, видалення чи інших операцій, які можуть спричинити помилку, якщо файл не знайдено.
string myFileToCheck = "report.txt";
// Спочатку створимо його для прикладу
File.WriteAllText(myFileToCheck, "Пробний звіт.");
if (File.Exists(myFileToCheck))
{
Console.WriteLine($"Файл '{myFileToCheck}' знайдено.");
// Тепер можна безпечно читати, видаляти тощо.
}
else
{
Console.WriteLine($"Файл '{myFileToCheck}' не знайдено.");
}
Видалення файлу: File.Delete(path) безповоротно видаляє файл із диска. Будьте дуже обережні: ця дія незворотна!
string fileToDelete = "temp_file.txt";
// Створимо файл для прикладу
File.WriteAllText(fileToDelete, "Цей файл буде видалено.");
if (File.Exists(fileToDelete))
{
File.Delete(fileToDelete);
Console.WriteLine($"Файл '{fileToDelete}' успішно видалено.");
}
else
{
Console.WriteLine($"Файл '{fileToDelete}' уже не існує або був видалений раніше.");
}
7. Винятки під час роботи з файлами
Робота з файлами завжди пов’язана з взаємодією з операційною системою та зовнішніми ресурсами, що робить її потенційно вразливою до помилок.
Обробляйте винятки (помилки)! Під час роботи з файлами може виникнути багато проблем, які ваша програма має коректно обробляти, а не просто аварійно завершуватися. Типові ситуації:
- FileNotFoundException: файл, до якого ви намагаєтеся звернутися, не існує за вказаним шляхом.
- DirectoryNotFoundException: каталог, у якому ви намагаєтеся створити або знайти файл, не існує.
- UnauthorizedAccessException: у вашої програми немає прав доступу для читання, запису чи видалення файлу/каталогу (наприклад, файл системний або розташований у захищеній області).
- IOException: загальна помилка введення/виведення; часто виникає, коли файл уже відкрито іншою програмою та заблоковано, або на диску немає місця.
Завжди використовуйте конструкцію try-catch для безпечної роботи з файлами. Це дає змогу перехопити помилку і виконати відповідні дії (наприклад, вивести повідомлення користувачу, записати в журнал).
string sensitiveFilePath = "secret_data.txt";
try
{
// Спроба прочитати файл, який, можливо, не існує або до якого немає доступу
string secretContent = File.ReadAllText(sensitiveFilePath);
Console.WriteLine($"Прочитано секретний вміст: {secretContent}");
}
catch (FileNotFoundException) // Конкретна помилка: файл не знайдено
{
Console.WriteLine($"Помилка: Файл '{sensitiveFilePath}' не знайдено. Переконайтеся, що шлях указано правильно.");
}
catch (IOException ex) // Загальна помилка введення/виведення (наприклад, файл заблоковано)
{
Console.WriteLine($"Помилка введення/виведення під час роботи з файлом '{sensitiveFilePath}': {ex.Message}");
}
catch (Exception ex) // Будь-яка інша неочікувана помилка
{
Console.WriteLine($"Сталася непередбачена помилка: {ex.Message}");
}
8. Коли System.IO.File — не найкращий вибір?
Хоч клас File і дуже універсальний, існують сценарії, де його використання не є оптимальним або навіть можливим:
Величезні файли (десятки гігабайтів і більше): Методи ReadAllText()/ReadAllBytes() завантажують весь файл у памʼять. Для вкрай великих файлів це призведе до OutOfMemoryException. У таких випадках File.ReadLines() (для тексту) або прямий підхід із потоками (Streams) (FileStream) є необхідними для читання/запису частинами. (Про потоки — у наступній лекції).
Тонке керування доступом до файла: Якщо вам потрібне дуже специфічне керування тим, як файл відкривається (наприклад, виключний доступ, щоб ніхто інший не міг його читати або записувати, або, навпаки, одночасний доступ для читання кількома процесами), File не надає такого рівня контролю. Для цього потрібні нижчорівневі класи потоків.
Асинхронні операції: Для високопродуктивних застосунків, яким потрібно виконувати файлові операції, не блокуючи основний потік (наприклад, у веб-серверах), потрібні асинхронні методи (наприклад, ReadAllTextAsync()), які теж базуються на потоках.
Часткове читання/запис: Якщо вам потрібно прочитати лише частину файлу з певного зміщення або записати дані у середину наявного файлу, методи File не підходять. Для цього потрібен прямий доступ до файлового потоку.
9. Резюме
Чому System.IO.File такий популярний?
- Простота й лаконічність: Його статичні методи дозволяють виконувати складні операції буквально в один‑два рядки коду. Це суттєво спрощує розробку й робить код більш читабельним.
- Автоматичне керування ресурсами: File сам дбає про відкриття й закриття файлів для кожної операції, позбавляючи вас необхідності вручну керувати ресурсами (що часто призводить до помилок).
- Універсальність для повсякденних завдань: Для більшості типових операцій з текстовими й бінарними файлами малих і середніх розмірів можливостей File більш ніж достатньо.
- Поширеність: У більшості практичних завдань і на співбесідах очікується, що ви впевнено користуєтеся методами класу File.
- Прихована складність: Усі методи написано так, щоб інкапсулювати складну логіку взаємодії з операційною системою, надаючи вам чистий і зрозумілий інтерфейс.
Часті запитання та уточнення
Який максимальний розмір файлу допустимий для File.ReadAllText()?
Для файлів розміром до кількох десятків мегабайт (< 100 МБ) зазвичай жодних проблем не виникає. Якщо файл більший (сотні МБ, гігабайти), використання File.ReadAllText() може призвести до вичерпання оперативної памʼяті (виняток OutOfMemoryException). У таких випадках слід використовувати File.ReadLines() для читання рядок за рядком або розширені інструменти для потокової обробки, про які ви дізнаєтеся пізніше.
Чи можна створити/записати файл у каталозі, якого немає?
Ні. Методи File.WriteAllText(), File.WriteAllBytes() та інші не створюють батьківські каталоги автоматично. Якщо ви вказуєте шлях C:\MyAppData\Logs\current.log, а каталоги MyAppData чи Logs не існують, виникне DirectoryNotFoundException. Спочатку потрібно переконатися, що всі потрібні каталоги існують, використовуючи методи класу System.IO.Directory (наприклад, Directory.CreateDirectory("myfolder")).
Як дізнатися дату створення, змінення чи розмір файлу?
Для отримання детальнішої інформації про файл — такої як дата створення, дата останньої зміни, розмір, атрибути (прихований, лише для читання тощо) — використовуйте клас System.IO.FileInfo. Він надає багато корисних властивостей.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ