C#. Текстові файли. Розв’язування задач на зміну текстових файлів. Класи File, StreamReader, StreamWriter

Текстові файли. Розв’язування задач на зміну текстових файлів. Класи File, StreamReader, StreamWriter

У даній темі наведено приклади рішення найбільш поширених задач, що виникають при обробці текстових файлів на мові C#. Приклади наведені для додатків, створених за шаблоном Console Application. Розв’язок кожної задачі представлений відповідною статичною функцією. За бажанням можна модифікувати продемонстровані статичні функції у функції екземпляру класу.

Для обробки текстових файлів продемонстровано роботу класів File, StreamReader, StreamWriter.


Зміст


Пошук на інших ресурсах:

1. Як визначити, що дані у текстовому файлі закінчились?

Читання даних з текстового файлу здійснюється з допомогою методу ReadLine(). Метод читає рядок символів з поточного потоку і повертає його у вигляді типу string. Ознакою закінчення рядка є символ ‘\n’ у файловому потоці. Якщо досягнуто кінець файлу, метод ReadLine() повертає значення null. Відповідна перевірка у програмі дозволяє визначити, чи прочитані усі дані з текстового файлу.

 

2. Послідовність дій при читанні даних з файлу. Використання класу StreamReader

Щоб прочитати дані з текстового файлу потрібно виконати приблизно таку послідовність дій:

1. Підключити модуль System.IO з допомогою рядка

using System.IO;

2. Створити екземпляр класу StreamReader з допомогою одного з перевантажених конструкторів. Одночасно зі створенням екземпляру відбувається відкриття файлу для читання або запису. Клас StreamReader має декілька конструкторів.

У нижченаведеному прикладі відкривається текстовий файл з іменем “TextFile1.txt”. Файл відкривається для читання (за замовчуванням).

// Відкрити текстовий файл для читання
StreamReader fileObj = new StreamReader("TextFile1.txt");

3. З допомогою екземпляру StreamReader здійснити циклічне читання рядків файлу як показано нижче

...

// прочитати один рядок
string s = fileObj.ReadLine();

while (s != null) // значення null - кінець файлу
{
  // Виконати дії над рядком s
  // ...

  // Прочитати наступний рядок
  s = fileObj.ReadLine();
}

...

4. Закрити файл з допомогою методу Close()

fileObj.Close()

 

3. Функція PrintFile(). Вивести вміст текстового файлу на екран. Спосіб 1

Для наочної демонстрації читання інформації з текстового файлу приводиться статична функція PrintFile(), яка виводить вміст текстового файлу на екран. Для читання рядків з файлу функція використовує метод ReadLine() класу StreamReader. Якщо досягнуто кінець файлу, то ReadLine() повертає null.

using System;
using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

namespace ConsoleApp7
{
  class Program
  {
    // Статична функція, яка виводить вміст файлу на екран.
    // Функція використовує метод ReadLine() для читання рядка з файлу.
    // Ім'я файлу задається вхідним параметром.
    // Якщо файл успішно прочитано, то функція повертає true.
    static bool PrintFile(string filename)
    {
      // 1. Створити екземпляр класу StreamReader.
      // Клас StreamReader призначений для читання з потоку символьних даних,
      // використовується для читання інформації з текстових файлів.
      StreamReader sr;

      // 2. Відкрити текстовий файл для читання
      try
      {
        // Спроба відкрити файл для читання (за замовчуванням)
        sr = new StreamReader(filename);
      }
      catch (FileNotFoundException e)
      {
        // Якщо спроба невдала, то вивести відповідне повідомлення
        // та завершити роботу функції
        WriteLine("Error: {0}", e.Message);
        return false;
      }

      // 3. Цикл читання рядків файлу та вивід їх на екран
      string s; // додаткова змінна

      WriteLine("The content of file {0}:", filename);
      s = sr.ReadLine(); // прочитати перший рядок з файлу
      while (s != null) // кінець файлу - значення null
      {
        // Вивести рядки на екран
        WriteLine(s);

        // Прочитати наступний рядок
        s = sr.ReadLine();
      }

      // 4. Закрити файл
      sr.Close();

      // 5. Вихід з успішним результатом
      return true;
    }

    static void Main(string[] args)
    {
      // Вивести вміст файлу "TextFile1.txt" на екран
      PrintFile("TextFile1.txt");
      ReadLine();
    }
  }
}

 

4. Функція PrintFile2(). Вивести вміст текстового файлу на екран. Спосіб 2

Нижче наведено приклад статичної функції PrintFile2(), яка використовує метод ReadToEnd() класу StreamReader для читання рядків з файлу. На відміну від ReadLine(), метод ReadToEnd() читає усі рядки з файлу і записує їх у рядок типу string включно з символами ‘\n’, ‘\r’.

Алгоритм роботи наступний:

  • спочатку читаються усі рядки з файлу в один рядок типу string;
  • для отриманого рядка викликається функція Split(), яка розбиває рядок на масив рядків. Ознакою розбиття рядків є символ ‘\n’;
  • вивести масив рядків на екран.

 

using System;
using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

namespace ConsoleApp7
{
  class Program
  {
    // Вивести вміст текстового файлу на екран.
    // Функція використовує метод ReadToEnd().
    static bool PrintFile2(string filename)
    {
      // 1. Створити екземпляр класу StreamReader.
      // Клас StreamReader призначений для читання з потоку символьних даних,
      // використовується для читання інформації з текстових файлів.
      StreamReader sr;

      // 2. Відкрити текстовий файл для читання
      try
      {
        // Спроба відкрити файл для читання (за замовчуванням)
        sr = new StreamReader(filename);
      }
      catch (FileNotFoundException e)
      {
        // Якщо спроба невдала, то вивести відповідне повідомлення
        // та завершити роботу функції
        WriteLine("Error: {0}", e.Message);
        return false;
      }

      // 3. Прочитати рядки з файлу в один рядок типу string
      string s = sr.ReadToEnd();

      // 4. Конвертувати рядки з файлу в масив типу string[]
      string[] lines = s.Split('\n'); // символ-розділювач - '\n'

      // 5. Вивести масив lines на екран
      for (int i = 0; i < lines.Length; i++)
        WriteLine("{0}", lines[i]);

      // 5. Закрити файл
      sr.Close();

      // 6. Вихід з успішним результатом
      return true;
    }

    static void Main(string[] args)
    {
      // Вивести вміст файлу "TextFile1.txt" на екран
      PrintFile2("TextFile1.txt");
      Console.ReadLine();
    }
  }
}

 

5. Функція ReplaceStringInFile(). Заміна рядка у текстовому файлі. Використання можливостей класу File

При операціях з файлами важливо дотримуватись правильної послідовності читання/запису. Якщо спробувати змінювати дані у файлі безпосередньо, то можна допустити багато невидинмих помилок. Тому, для заміни рядка у текстовому файлі рекомендується використати наступний алгоритм:

  • зчитати усі рядки з файлу в масив. У прикладі для читання рядків з файлу використовується метод ReadAllLines() статичного класу File. Цей метод повертає масив типу string[];
  • здійснити заміну рядка в масиві;
  • записати рядки з масиву знову в файл. Для запису використовується метод WriteAllLines() статичного класу File. У метод передається ім’я файлу та масив рядків типу string[], який потрібно записати.

Текст демонстраційного консольного додатку наведено нижче.

using System;
using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

namespace ConsoleApp7
{
  class Program
  {
    // Функція, що замінює рядок у текстовому файлі.
    // Параметри:
    // - filename - ім'я файлу;
    // - position - позиція, в якій потрібно замінити рядок (нумерується з 0);
    // - str - рядок заміни.
    // Функція повертає true, якщо заміна рядка у файлі відбулась успішно.
    static bool ReplaceStringInFile(string filename, int position, string str)
    {
      // 1. Перевірка, чи файл присутній.
      // Використовується статична функція Exists().
      if (!File.Exists(filename)) return false;

      // 2. Отримати дані з файлу у вигляді масиву рядків.
      // Для цього використовується метод ReadAllLines().
      string[] arrayS = File.ReadAllLines(filename);

      // 3. Перевірка, чи коректне значення позиції рядка
      if ((position < 0) || (position >= arrayS.Length))
      return false;

      // 4. Замінити рядок
      arrayS[position] = str;

      // 5. Записати масив рядків у файл filename
      File.WriteAllLines(filename, arrayS);

      // 6. Повернути результат
      return true;
    }

    static void Main(string[] args)
    {
      // Заміна рядка у файлі
      // 1. Оголосити змінні
      string str;
      int pos;

      // 2. Ввести вхідні дані
      Console.WriteLine("Enter string:");
      str = Console.ReadLine();
      Console.WriteLine("Enter position:");
      pos = Convert.ToInt32(Console.ReadLine());

      // 3. Використання функції ReplaceStringInFile
      if (ReplaceStringInFile("TextFile1.txt", pos, str))
          Console.WriteLine("Ok!");
      else
          Console.WriteLine("Error.");

      ReadLine();
    }
  }
}

 

6. Функція RemoveStringFromFile(). Видалення рядка з файлу за його індексом. Приклад

При маніпуляціях з файлами, які змінюють загальну кількість рядків у файлі (вставка, видалення), краще представляти прочитану інформацію у вигляді списків типу List<string>. Це пов’язано з тим, що в списки зручніше вставляти/видаляти елементи в конкретні позиції. Список не потребує виділення додаткових (часто великих) масивів інформації на відміну від масиву.

У даному прикладі продемонстровано використання функції ReadLine() класу StreamReader. При читанні рядка з файлу ця функція видаляє символи ‘\r’, ‘n’, які потребують додаткової обробки.

using System;
using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

// Для використання списку List<string> потрібно підключити модуль Generic
using System.Collections.Generic;

namespace ConsoleApp7
{
  class Program
  {
    // Функція, що реалізує видалення рядка з файлу за його індексом.
    // Параметри:
    // - filename - ім'я файлу;
    // - index - позиція рядка, який потрібно видалити (нумерується з 0).
    // Якщо видалення відбулось успішно, функція повертає true.
    static bool RemoveLineFromFileByIndex(string filename, int index)
    {
      // 1. Оголосити файлову змінну
      StreamReader sr;

      // 2. Спроба відкрити файл filename
      try
      {
        // Використати метод OpenText() класу File
        sr = File.OpenText(filename);
      }
      catch(FileNotFoundException e)
      {
        // Якщо файл не знайдено, то обробити виключення
        WriteLine(e.Message);
        return false;
      }

      // 3. Зчитати усі рядки з файлу в список рядків lst
      List<string> lst = new List<string>(0); // створити пустий список
      string s;
      while ((s = sr.ReadLine()) != null)
        lst.Add(s); // додати рядок в список

      // 4. Файл більше не потрібен, тому його можна закрити
      sr.Close();

      // 5. Перевірка, чи коректне значення index
      if ((index < 0) || (index >= lst.Count))
        return false;

      // 6. Видалити рядок зі списку в позиції index
      lst.RemoveAt(index);

      // 7. Записати список lst знову у файл. Для цього потрібно
      // використати можливості класу StreamWriter
      // Створити екземпляр класу StreamWriter
      StreamWriter sw = new StreamWriter(filename); // відкрити файл для запису

      // Запис списку у файл
      for (int i = 0; i < lst.Count; i++)
        sw.WriteLine(lst[i]);

      // 8. Закрити файл
      sw.Close();

      // 9. Повернути результат
      return true;
    }

    static void Main(string[] args)
    {
      // Видалення рядка у файлі
      // 1. Оголосити змінні
      int index;

      // 2. Ввести вхідні дані
      WriteLine("Enter position:");
      index = Convert.ToInt32(Console.ReadLine());

      // 3. Використання функції RemoveLineFromFileByIndex()
      if (RemoveLineFromFileByIndex("TextFile1.txt", index))
        WriteLine("Ok!");
      else
        WriteLine("Error.");

      ReadLine();
    }
  }
}

 

7. Функція InsertLineInFile(). Вставка рядка у файл в заданій позиції

За зразком пункту 6 можна реалізувати вставку рядка у файл в заданій позиції. Рядки у файлі попередньо копіюються в список типу List<string>. Використання списків у подібних задачах полегшує роботу програміста, оскільки списки містять всі необхідні методи вставки, додавання, видалення елементу.

Запропонована функція InsertLineInFile() має одну особливість. Якщо позиція вставки (нумерується з 0) рівна кількості елементів у файлі n, то рядок вставки додається в кінець файлу. Таким чином функція реалізує вставку та додавання рядка у файл.

// Функція, що реалізує вставку рядка у файл в заданій позиції
// Параметри:
// - filename - ім'я файлу (повне або скорочене);
// - pos - позиція вставки;
// - str - рядок, що вставляється.
// Функція повертає true, якщо вставка успішна.
static bool InsertLineInFile(string filename, int position, string str)
{
  // 1. Оголосити файлову змінну
  StreamReader sr;

  // 2. Спроба відкрити файл filename
  try
  {
    // Використати конструктор класу StreamReader
    sr = new StreamReader(filename);
  }
  catch (FileNotFoundException e)
  {
    // Якщо файл не знайдено, то обробити виключення
    WriteLine(e.Message);
    return false;
  }

  // 3. Зчитати усі рядки з файлу в список рядків lst
  List<string> lst = new List<string>(0); // створити пустий список
  string s;
  while ((s = sr.ReadLine()) != null)
    lst.Add(s); // додати рядок в список

  // 4. Файл більше не потрібен, тому його можна закрити
  sr.Close();

  // 5. Перевірка, чи коректне значення position
  if ((position < 0) || (position > lst.Count))
    return false;

  // 6. Реалізувати вставку в список
  // Якщо position рівне кількості рядків у списку lst, то додати рядок
  // в кінець списку
  if (position == lst.Count)
    lst.Add(str);
  else
    lst.Insert(position, str);

  // 7. Записати список знову в файл
  // 7.1. Створити екземпляр класу StreamWriter
  StreamWriter sw = new StreamWriter(filename);

  // 7.2. Цикл запису списку в файл
  foreach (string ts in lst)
    sw.WriteLine(ts);

  // 7.3. Закрити файл
  sw.Close();

  // 8. Повернути результат
  return true;
}

 

8. Функція ConvertLinesFileToList(). Конвертувати рядки файлу в список List<string>

В пунктах 6, 7 повторюється код, що утворює список типу List<string> з рядків файлу. Тому, доцільно написати функцію ConvertLinesFileToList(), яка реалізує конвертування рядків файлу в список. Текст функції наступний.

// Функція, що конвертує рядки файлу в список List<string>
// Параметри:
// - filename - ім'я файлу;
// - LS - список рядків, який отримується читанням з файлу.
// Якщо конвертування відбулось успішно, функція повертає true.
static bool ConvertLinesFileToList(string filename, out List<string> LS)
{
  // 1. Оголосити файлову змінну
  StreamReader sr;

  // 2. Відкрити файл
  try
  {
    // Спроба відкрити файл для читання
    sr = new StreamReader(filename);
  }
  catch(FileNotFoundException e)
  {
    // Якщо файл не знайдено, то обробити виключення
    WriteLine(e.Message);
    LS = null;
    return false;
  }

  // 3. Зчитати рядки файлу в список LS
  LS = new List<string>(0);
  string s;

  // цикл читання рядків з файлу та утворення списку
  while ((s = sr.ReadLine()) != null)
    LS.Add(s);

  // 4. Закрити файл
  sr.Close();

  // 5. Повернути результат
  return true;
}

 

9. Функція AddArrayLinesInFile(). Додавання масиву рядків в кінець файлу

У прикладі наведено функцію, яка додає масив рядків в кінець файлу. Дана функція використовує функцію ConvertLinesFileToList(), яка повертає рядки файлу у вигляді списку і описується в пункті 8. Для роботи з файлами використовуються можливості класів StreamReader та StreamWriter.

Алгоритм методу наступний:

  • прочитати дані з файлу в список типу List<string>;
  • додати до списку масив рядків;
  • записати змінений список у файл. Для запису списку використовуються засоби класу StreamWriter.

Програмний код функції AddArrayLinesInFile() наступний.

using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

// Для використання списку List<string> потрібно підключити модуль Generic
using System.Collections.Generic;

...

// Функція, що додає масив рядків в кінець файлу.
// Параметри:
// - filename - ім'я файлу;
// - arrayS - масив рядків, які додаються в кінець файлу.
static bool AddArrayLinesInFile(string filename, string[] arrayS)
{
  // 1. Отримати список рядків файлу
  List<string> lst;

  // викликати функцію з п.8
  if (!ConvertLinesFileToList(filename, out lst))
  {
    return false;
  }

  // 2. Додати масив рядків до списку lst
  foreach (string s in arrayS)
    lst.Add(s);

  // 3. Записати список lst в файл
  // 3.1. Створити екземпляр класу StreamWriter
  StreamWriter sw = new StreamWriter(filename);

  // 3.2. Цикл запису списку в файл
  foreach (string ts in lst)
    sw.WriteLine(ts);

  // 3.3. Закрити файл
  sw.Close();

  // 4. Повернути результат
  return true;
}

Використання функції в іншому програмному коді може бути, наприклад, таким.

static void Main(string[] args)
{
  // Додавання масиву рядків в кінець файлу
  // 1. Масив рядків
  string[] arrS = { "abcd", "jklmn", "oprst" };

  // 2. Використання функції AddArrayLinesInFile()
  if (AddArrayLinesInFile("TextFile1.txt", arrS))
    WriteLine("Ok!");
  else
    WriteLine("Error.");

  ReadLine();
}

 

10. Функція SwapLinesInFile(). Обмін місцями двох рядків у файлі. Приклад

Обмін місцями двох рядків у файлі не змінює загальну кількість рядків. Тому, прочитані рядки з файлу доцільно представити масивом типу string[].

У C# в статичному класі File є корисна функція ReadAllLines(), яка повертає рядки файлу у вигляді масиву string[]. Як і у випадку з функцією ReadLine() класу StreamReader, кожен рядок у функції ReadAllLines() не містить символів ‘\n’ та ‘\r’.

Статичний файл File також містить функцію WriteAllLines(), яка записує рядки типу string[] у файл.

Нижче наведено фрагмент коду, що містить опис функції SwapLinesInFile().

using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;
using System;

...

// Функція, що міняє місцями два рядки у файлі
// Параметри:
// - filename - ім'я файлу
// - pos1, pos2 - позиції рядків, що обмінюються (нумеруються з 0).
// Функція повертає true, якщо обмін рядків відбувся успішно.
static bool SwapLinesInFile(string filename, int pos1, int pos2)
{
  // 1. Перевірка, чи файл існує
  if (!File.Exists(filename)) return false;

  // 2. Зчитати всі рядки з файлу в масив arrayS
  string[] arrayS = File.ReadAllLines(filename);

  // 3. Перевірка, чи значення pos1, pos2 коректні
  if ((pos1 < 0) || (pos1 >= arrayS.Length)) return false;
  if ((pos2 < 0) || (pos2 >= arrayS.Length)) return false;

  // 4. Обміняти місцями рядки в масиві arrayS
  string s = arrayS[pos1];
  arrayS[pos1] = arrayS[pos2];
  arrayS[pos2] = s;

  // 5. Записати змінений масив arrayS знову в файл
  File.WriteAllLines(filename, arrayS);

  // 6. Повернути результат
  return true;
}

Використання функції SwapLinesInFile() в іншому коді може бути, наприклад, таким

static void Main(string[] args)
{
  // Обмін місцями двох рядків у файлі
  // 1. Ввести позиції рядків у файлі
  int pos1, pos2;

  Write("pos1 = ");
  pos1 = Int32.Parse(ReadLine());
  Write("pos2 = ");
  pos2 = Int32.Parse(ReadLine());

  // 2. Використання функції
  if (SwapLinesInFile("TextFile1.txt", pos1, pos2))
    WriteLine("Ok!");
  else
    WriteLine("Error.");

  ReadLine();
}

 

11. Функція ReverseStringsInFile(). Реверсування рядків файлу (перестановка рядків файлу у зворотному порядку)

Алгоритм функції наступний:

  • прочитати рядки файлу в масив типу string[] з допомогою функції File.ReadAllLines();
  • з допомогою циклу реверсувати масив рядків;
  • записати реверсовані рядки знову в файл.

Текст функції ReverseStringsInFile() наступний

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

// Функція, яка змінює порядок рядків у файлі на зворотній.
// Функція повертає true, якщо реверсування відбулось успішно
static bool ReverseAllLinesInFile(string filename)
{
  // 1. Перевірка, чи файл існує
  if (!File.Exists(filename)) return false;

  // 2. Зчитати всі рядки з файлу в масив arrayS
  string[] arrayS = File.ReadAllLines(filename);
  string s;

  // 3. Реверсувати рядки в масиві arrayS
  for (int i = 0; i < arrayS.Length / 2; i++)
  {
    s = arrayS[i];
    arrayS[i] = arrayS[arrayS.Length - i - 1];
    arrayS[arrayS.Length - i - 1] = s;
  }

  // 4. Записати змінений масив arrayS знову в файл
  File.WriteAllLines(filename, arrayS);

  // 5. Повернути результат
  return true;
}

Використання функції може бути, наприклад, таким

static void Main(string[] args)
{
  // Реверсування рядків у файлі
  if (ReverseAllLinesInFile("TextFile1.txt"))
    WriteLine("Ok!");
  else
    WriteLine("Error.");

  ReadLine();
}

 

12. Функція SortLineInFileBubble(). Сортування рядків у файлі методом бульбашки. Приклад

 

using static System.Console;

// Для роботи з файлами потрібно підключити модуль IO
using System.IO;

...

// Функція, яка сортує рядки у файлі методом бульбашки.
// Функція повертає true, якщо сортування успішне
static bool SortAllLinesInFileBubble(string filename)
{
  // 1. Перевірка, чи файл існує
  if (!File.Exists(filename)) return false;

  // 2. Зчитати всі рядки з файлу в масив arrayS
  string[] arrayS = File.ReadAllLines(filename);
  string s;

  // 3. Посортувати рядки у масиві arrayS методом бульбашки
  for (int i = 0; i < arrayS.Length; i++)
    for (int j = arrayS.Length - 1; j > i; j--)
      if (arrayS[j - 1].CompareTo(arrayS[j]) > 0) // сортування за зростанням
      {
        s = arrayS[j - 1];
        arrayS[j - 1] = arrayS[j];
        arrayS[j] = s;
      }

  // 4. Записати змінений масив arrayS знову в файл
  File.WriteAllLines(filename, arrayS);

  // 5. Повернути результат
  return true;
}

Використання функції SortAllLinesInFileBubble() у функції main()

static void Main(string[] args)
{
  // Сортування рядків у файлі
  if (SortAllLinesInFileBubble("TextFile1.txt"))
    WriteLine("Ok!");
  else
    WriteLine("Error.");

  ReadLine();
}

 


Зв’язані теми