C++. Примеры работы с текстовыми файлами. Модификация файлов. Сортировка данных в файлах. Конвертирование данных файла в список

Примеры работы с текстовыми файлами. Модификация файлов. Сортировка данных в файлах. Конвертирование данных файла в массив

В данной теме наводятся примеры решения известных задач, возникающих при обработке текстовых файлов на языке C++. Проработав решения этих задач, вы научитесь обрабатывать массивы строчных данных, управлять распределением памяти а также использовать средства C++ для работы с текстовыми файлами.


Содержание


Поиск на других ресурсах:




1. Как определить, что данные в текстовом файле закончились?

Для определения конца файла используется функция eof(). Функция возвращает результат типа bool. Прототип функции следующий

bool std::eof();

здесь std – пространство имен, в котором объявлена функция eof().

Если при вызове функции текущий указатель чтения указывает на конец файла, то функция возвращает true. В противном случае функция возвращает false.

Пример 1.

Ниже приведен фрагмент кода, в котором используется функция eof() для определения, достигнут ли конец файла, которому соответствует экземпляр с именем inputFile

if (inputFile.eof() == false)
{
  cout << "End of file not reached.";
}
else
{
  cout << "End of file is reached.";
}

Пример 2.

Чаще всего функция eof() используется в цикле while при чтении строк файла

while (!inputFile.eof()) // пока не конец файла
{
  // Считывание строк из файла
  // ...
}

Как только будет достигнут конец файла inputFile, то произойдет выход из цикла.

  ⇑

2. Функция CountLinesInFile(). Подсчет количества строк в текстовом файле

Функция CountLinesInFile() возвращает количество строк в текстовом файле.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, определяющая количество строк в файле.
// Имя файла задается входным параметром
int CountLinesInFile(char* filename)
{
  // 1. Объявить экземпляр F, который связан с файлом filename.
  // Файл открывается для чтения в текстовом формате.
  ifstream F(filename, ios::in);

  // 2. Проверка, существует ли файл
  if (!F)
  {
    return -1;
  }

  // 3. Вычислить количество строк в файле
  // 3.1. Объявить дополнительные переменные
  int count = 0;
  char buffer[1000]; // буфер для сохранения одной строки

  // 3.2. Цикл чтения строк.
  // Каждая строка файла читается функцией getline().
  // Проверка конца файла осуществляется функцией eof().
  while (!F.eof())
  {
    // Увеличить счетчик строк
    count++;

    // Считать одну строку в буфер
    F.getline(buffer, 1000);
  }

  // 4. Закрыть файл F
  F.close();

  // 5. Вернуть результат
  return count;
}

void main()
{
  // Определение количества строк в файле "TextFile1.txt"
  int nLines = CountLinesInFile((char*)"TextFile1.txt");

  // Вывод результата
  if (nLines == -1)
    cout << "Error opening file";
  else
    cout << "nLines = " << nLines << endl;
}

  ⇑

3. Функция GetStringsFromFileC(). Получить массив (список) строк типа char* из текстового файла

Список строк можно вернуть одним из двух способов, в которых одна строка воспринимается по разному:

  • как тип char*. В этом случае список строк имеет тип char**;
  • как тип string – список строк имеет тип string*.

Для определения количества строк в файле функция обращается к функции CountLinesInFile(), которая описывается в пункте 2.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, которая возвращает строки файла в виде списка типа char**.
// Параметры:
// - filename - имя файла;
// - _lines - список строк файла типа (char**).
// Функция возвращает общее количество прочитанных строк в файле,
// если чтение произошло неудачно, то функция возвращает -1.
int GetStringsFromFileC(string filename, char*** _lines = nullptr)
{
  // 1. Дополнительные переменные
  char** lines;
  int n = CountLinesInFile(filename); // получить количество строк в файле

  // 2. Проверка, известно ли количество строк
  if (n == -1) return -1;

  // 3. Объявить файловую переменную
  ifstream F(filename); // открыть файл для чтения

  // 4. Проверка, открылся ли файл
  if (!F) return -1;

  // 5. Попытка выделить память для n строк
  try
  {
    lines = new char* [n];
  }
  catch (bad_alloc e)
  {
    // если невозможно выделить память, то выход
    cout << e.what() << endl; // вывести сообщение об ошибке
    F.close(); // закрыть файл
    return -1;
  }

  // 6. Цикл чтения строк и выделения памяти для них
  int len; // длина одной строки
  char buffer[1000]; // память, куда записывается одна строка из файла

  for (int i = 0; i < n; i++)
  {
    // 6.1. Считать строку из файла
    F.getline(buffer, 1000);

    // 6.2. Определить длину прочитанной строки
    for (len = 0; buffer[len] != '\0'; len++);

    // 6.3. Выделить память для len+1 символов
    lines[i] = new char[len + 1];

    // 6.4. Скопировать данные из buffer в lines[i]
    for (int j = 0; j < len; j++)
      lines[i][j] = buffer[j];
    lines[i][len] = '\0'; // добавить символ конца строки
  }

  // 7. Закрыть файл
  F.close();

  // 8. Записать результат
  *_lines = lines;
  return n;
}

Использование функции GetStringsFromFileC() может быть, например, следующим:

void main()
{
  // Вывод списка строк файла на екран
  // 1. Объявить внутренние переменные
  int count; // количество строк
  char** lines = nullptr; // список строк типа char*

  // 2. Получить список строк типа char**
  count = GetStringsFromFileC("TextFile1.txt", &lines);

  // 3. Проверка, получен ли список
  if (count < 0)
  {
    cout << "Error" << endl;
    return;
  }

  // 3. Вывести список строк на экран
  for (int i = 0; i < count; i++)
  {
    cout << lines[i] << endl;
  }

  // 4. Освободить память, выделенную для каждой строки lines
  if (lines!=nullptr)
    for (int i = 0; i < count; i++)
      delete lines[i];

  // 5. Освободить память, выделенную для массива lines
  if (lines != nullptr)
    delete[] lines;
}

  ⇑

4. Функция GetStringsFromFileS(). Получить массив строк типа string из текстового файла

Работа с типом string более удобна чем с типом char*. В следующем фрагменте кода реализована функция GetStringsFromFileS(), читающая строки из текстового файла и записывающая их в массив (список) типа string. Данная функция использует функцию CountLinesInFile(), которая описывается в пункте 2 данной темы.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, получающая строки файла в виде списка типа string*
// Параметры:
// - filename - имя файла;
// - _lines - список строк файла типа (string*).
// Функция возвращает общее количество прочитанных строк.
int GetStringsFromFileS(string filename, string** _lines)
{
  // 1. Дополнительные переменные
  string* lines; // временный список строк
  int n = CountLinesInFile(filename); // Получить количество строк в файле

  // 2. Проверка, правильно ли прочитаны строки из файла
  if (n == -1) return -1;

  // 3. Объявить файловую переменную и открыть файл filename для чтения
  ifstream F(filename);

  // 4. Проверка, открыт ли файл
  if (!F) return -1;

  // 5. Попытка выделить память для n строк типа string
  try
  {
    lines = new string[n];
  }
  catch (bad_alloc e)
  {
    cout << e.what() << endl; // вывести сообщение об ошибке
    F.close();
    return -2; // возврат с кодом -2
  }

  // 6. Чтение строк из файла и запись строк в список lines
  char buffer[1000]; // буфер для чтения строки

  for (int i = 0; i < n; i++)
  {
    // 6.1. Прочитать строку из файла в буфер buffer
    F.getline(buffer, 1000);

    // 6.2. Вычислить длину прочитанной строки
    int len;
    for (len = 0; buffer[len] != '\0'; len++);

    // 6.3. Записать buffer => lines[i], использовать метод assign().
    // Скопировать len байт из buffer в lines[i].
    lines[i].assign(buffer, len);
  }

  // 7. Закрыть файл
  F.close();

  // 8. Вернуть результат
  *_lines = lines;
  return n;
}

Использование функции GetStringsFromFileS() может быть, например, следующим

void main()
{
  // Вывод списка строк файла на экран
  // 1. Объявить внутренние переменные
  int count; // количество строк
  string* lines = nullptr; // список строк типа string

  // 2. Получить список строк типа string*
  count = GetStringsFromFileS("TextFile1.txt", &lines);

  // 3. Проверка, получен ли список
  if (count < 0)
  {
    cout << "Error" << endl;
    return;
  }

  // 3. Вывести список строк на экран
  for (int i = 0; i < count; i++)
  {
    cout << lines[i] << endl;
  }

  // 4. Освободить память, выделенную для массива lines
  if (lines != nullptr)
    delete[] lines;
}

Результат работы программы (содержимое файла TextFile1.txt):

#include <iostream>

using namespace std;

void main()
{
  cout << "Hello world!";
}

  ⇑

5. Функция SetStringsToFileS(). Записать массив (список) строк типа string в текстовый файл

При работе с текстовыми файлами полезна функция SaveStringsToFileS(), которая записывает массив строк в файл. В нижеследующих примерах эта функция будет использоваться для записи модифициованного списка в файл.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, которая записывает массив строк типа string в текстовый файл.
// Параметры:
// - filename - имя файла;
// - lines - массив строк, которые записываются в файл;
// - count - количество строк.
// Функция возвращает true, если запись строк произошла успешно.
bool SetStringsToFileS(string filename, string* lines, int count)
{
  // 1. Объявить дополнительные переменные
  ofstream F(filename); // открыть файл для записи

  // 2. Проверка, успешно ли открылся файл
  if (!F) return false;

  // 3. Запись строк в файл кроме последней строки
  for (int i = 0; i < count - 1; i++)
    F << lines[i] << endl;

  // 4. Последнюю строку записать как есть (без символа '\n')
  F << lines[count - 1];

  // 5. Закрыть файл
  F.close();

  // 6. Вернуть результат
  return true;
}

Ниже приведен пример использования функции SetStringsToFileS().

void main()
{
  // Запись массива строк в файл
  // 1. Объявить внутренние переменные
  int count; // количество строк
  string* lines = nullptr; // список строк типа string

  // 2. Сформировать строки
  try
  {
    // попытка выделить память для 5 строк
    lines = new string[5];
  }
  catch (bad_alloc e)
  {
    cout << e.what() << endl;
    return;
  }

  // записать в строки некоторый текст
  lines[0] = "#include <stdio.h>";
  lines[1] = "using namespace std";
  lines[2] = "void main()";
  lines[3] = "{";
  lines[4] = "}";

  // 3. Записать строки в файл TextFile2.txt
  if (SetStringsToFileS("TextFile2.txt", lines, 5))
    cout << "OK!" << endl;
  else
    cout << "Error." << endl;

  // 4. Освободить память, выделенную для массива lines
  if (lines != nullptr)
    delete[] lines;
}

  ⇑

6. Функция ChangeStringInFileC(). Замена строки в текстовом файле

В примере приведена функция, заменяющая строку в файле в заданной позиции. Данная функция использует следующие функции:

  • CountLinesInFile() — определяет количество строк в файле (реализована в п. 3);
  • GetStringsFromFileC() — возвращает строки файла в виде массива (реализована в п. 4). Строки представлены типом char*.

Данная функция не использует дополнительный файл для копирования. Функция получает строки файла в виде списка lines, которые служат копией файла. Затем происходит запись этих строк в этот же файл за исключением строки, которую нужно заменить в файле. В позиции замены записывается входная строка замены. Таким образом происходит замена строки в текстовом файле.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, заменяющая строку в файле.
// Параметры:
// - filename - имя файла;
// - position - позиция строки в файле, которую нужно заменить;
// - str - строка, заменяющая строку в файле.
// Функция возвращает true, если операция замены строки прошла успешно.
bool ChangeStringInFileC(string filename, int position, string str)
{
  // 1. Получить строки файла в виде списка
  char** lines; // список строк файла
  int count; // количество строк файла
  count = GetStringsFromFileC(filename, &lines); // получить список lines

  // 2. Проверка, корректно ли прочитан файл
  if (count < 0) return false;

  // 3. Проверка, корректна ли позиция 0 <= position < count
  if ((position < 0) || (position >= count)) return false;

  // 4. Запись строк lines в файл до позиции position
  ofstream F(filename); // открыть файл для записи

  // 5. Проверка, открылся ли файл корректно - функция is_open()
  if (!F.is_open()) return false;

  for (int i = 0; i < position; i++)
    F << lines[i] << endl; // вывести строку в файл

  // 6. Запись строки с позиции position
  F << str.c_str() << endl; // здесь пишется строка str

  // 7. Запись строк после позиции position
  for (int i = position + 1; i < count - 1; i++)
    F << lines[i] << endl;

  // 8. Записать последнюю строку без символа '\n'
  F << lines[count - 1];

  // 9. Закрыть файл
  F.close();

  // 10. Освободить память, выделенную для строк
  for (int i = 0; i < count; i++)
    delete lines[i];

  // 11. Освободить указатели на строки
  delete[] lines;
}

Использование данной функции може быть, например, следующим:

void main()
{
  // Замена входной строки в файле
  string filename = "TextFile1.txt"; // исходный файл
  int pos = 5; // исходная позиция строки
  string str = "Hello world!"; // Исходная строка замены

  if (ChangeStringInFileC(filename, pos, str))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

По желанию, можно изменить ввод входных данных (имя файла, позиция, строка замены).

  ⇑

7. Функция RemoveStringFromFileByIndex(). Удаление строки из файла по его номеру

Удаление строки из файла по его номеру можно выполнять по примеру п. 6 (замена строки в текстовом файле).
Общий алгоритм следующий:

  • прочитать все строки файла в массив (список);
  • удалить строку из массива;
  • записать модифицированный массив обратно в файл.

Для формирования списка строк можно использовать функции GetStringsFromFileC() и GetStringsFromFileS(), которые описаны в пунктах 3 и 4.

Текст функции удаления строки по его номеру следующий.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, удаляющая строку в файле по заданному номеру,
// номер строки начинается с 0.
// Параметры функции:
// - filename - имя файла, из которого удаляется строка;
// - position - номер строки в файле, которую нужно удалить.
// Если операция прошла успешно, функция возвращает true, если
// строка не удалена, функция возвращает false.
bool RemoveStringFromFileByIndex(string filename, int position)
{
  // 1. Проверка, правильно ли указана строка
  if (position < 0) return false;

  // 2. Дополнительные переменные
  string* lines; // список строк
  int n; // количество строк в списке (файле)

  // 3. Прочитать количество строк в файле
  n = CountLinesInFile(filename); // вызов функции из пункта 2

  // 4. Проверка, прочитался ли файл filename
  if (n == -1) return false;

  // 5. Проверка, корректно ли значение position
  if (position >= n) return false;

  // 6. Получить список строк lines типа string*
  n = GetStringsFromFileS(filename, &lines); // функция из пункта 4

  // 7. Удалить строку в позиции position
  for (int i = position; i < n - 1; i++)
    lines[i] = lines[i + 1];

  // 8. Уменьшить общее количество строк
  n--;

  // 9. Записать список в файл - использовать функцию SetStringsToFileS()
  bool res = SetStringsToFileS(filename, lines, n); // запись списка

  // 10. Освободить массив lines
  if (n > 0) delete[] lines;

  // 11. Вернуть результат
  return res;
}

Использование функции может быть, например, следующим

void main()
{
  // Удаление исходной строки в файле
  string filename = "TextFile1.txt"; // исходный файл
  int pos = 7; // указанная позиция строки

  if (RemoveStringFromFileByIndex(filename,pos))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

  ⇑

8. Функция InsertStringToFile(). Вставка строки в заданную позицию в файле

Подход такой же, как в предыдущих пунктах. Нужно получить строки файла в виде массива (тип string или char*). Затем нужно вставить строку в массив и записать измененный массив строк обратно в файл.
Если значение позиции вставки равно количеству элементов в файле, то строка добавляется в конец файла.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, которая вставляет указанную строку в указанною позицию файла.
// Параметры функции:
// - filename - імя файла;
// - str - строка, которая вставляется;
// - position - позиция строки str. Нумеруется с 0 до количества строк в файле.
// Если вставка произошла успешно, функция возвращает true.
bool InsertStringToFile(string filename, string str, int position)
{
  // 1. Объявить дополнительные переменные
  int count;
  string* lines = nullptr; // список строк файла

  // 2. Получить список строк из файла - вызов функции, описанной в п.4
  count = GetStringsFromFileS(filename, &lines);

  // 3. Проверка, происходит ли нормально процесс чтения строк
  if ((count < 0)) return false;

  // 4. Проверка, корректно ли значение position.
  // Если positon==count, то строка добавляется в конец файла.
  if ((position < 0) || (position > count)) return false;

  // 5. Создать новый список lines2, в котором на 1 элемент больше
  string* lines2 = nullptr;
  try
  {
    // попытка выделить память для массива lines2
    lines2 = new string[count + 1];
  }
  catch (bad_alloc e)
  {
    // если попытка выделения памяти неудачна, то завершить программу
    cout << e.what() << endl;
    if (lines != nullptr) // освободить память
      delete[] lines;
    return false;
  }

  // 6. Скопировать lines=>lines2
  // 6.1. Сначала скопировать до позиции position
  for (int i = 0; i < position; i++)
    lines2[i] = lines[i];

  // 6.2. Добавить к lines2[position] строку str
  lines2[position] = str;

  // 6.3. Скопировать следующие строки
  for (int i = position; i < count; i++)
    lines2[i + 1] = lines[i];

  // 7. Увеличить общее количество строк на 1
  count++;

  // 8. Записать строки lines в файл
  bool res = SetStringsToFileS(filename, lines2, count);

  // 9. Освободить память, выделенную для строк lines, lines2
  if (lines != nullptr) delete[] lines;
  if (lines2 != nullptr) delete[] lines2;

  // 10. Вернуть результат
  return res;
}

Вызов функции InsertStringToFile() в функции main() может быть следующим.

void main()
{
  // Вставка указанной строки в заданную позицию файла
  string filename = "TextFile1.txt"; // исходный файл
  int pos = 8; // указанная позиция строки
  string s = "Hello"; // строка вставки

  if (InsertStringToFile(filename, s, pos))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

  ⇑

9. Функция SwapStringsInFile(). Обмен местами двух строк в файле

Сокращенный алгоритм функции SwapStringsInFile() следующий:

  1. Прочитать строки файла в список.
  2. Поменять строки в списке.
  3. Записать измененный список обратно в файл.

Текст функции обмена строк следующий.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, которая меняет местами две строки в указанных позициях.
// Параметры:
// - filename - имя файла;
// - pos1 - позиция строки обмена 1 (нумеруется с 0);
// - pos2 - позиция строки обмена 2 (нумеруется с 0).
// Если обмен прошел успешно, то функция возвращает true. Иначе false.
bool SwapStringsInFile(string filename, int pos1, int pos2)
{
  // 1. Проверка, корректны ли значения pos1, pos2
  if ((pos1 < 0) || (pos2 < 0)) return false;

  // 2. Объявить дополнительные переменные
  int count;
  string* lines = nullptr; // массив строк файла
  string s;

  // 3. Получить массив строк файла
  count = GetStringsFromFileS(filename, &lines); // функция из п.4

  // 4. Проверка, произошло ли успешно чтение из файла
  if (count < 0) return false;

  // 5. Проверка, лежат ли значения pos1, pos2 в пределах count
  if (pos1 >= count) return false;
  if (pos2 >= count) return false;

  // 6. Поменять местами строки lines[pos1] и lines[pos2]
  s = lines[pos1];
  lines[pos1] = lines[pos2];
  lines[pos2] = s;

  // 7. Записать массив lines обратно в файл
  bool res = SetStringsToFileS(filename, lines, count);

  // 8. Освободить память, выделенную для массива lines
  if (lines != nullptr)
    delete[] lines;

  // 9. Вернуть результат
  return res;
}

Вызов функции SwapStringsInFile() из функции main() может быть следующим

void main()
{
  // Обмен местами двух строк в файле
  string filename = "TextFile1.txt"; // исходный файл
  int pos1 = 2; // исходная позиция строки 1
  int pos2 = 7; // исходная позиция строки 2

  if (SwapStringsInFile(filename,pos1, pos2))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

  ⇑

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

Алгоритм метода следующий:

  • считать строки из файла и записать их в массив;
  • обменять местами элементы массива так, чтобы строки массива размещались в обратном порядке;
  • записать измененный массив снова в файл

Текст функции ReverseStringsInFile() следующий

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция, которая обращает строки файла в обратном порядке.
// Параметры:
// - filename - имя файла.
// Если операция произошла успешно, то функция возвращает true.
bool ReverseStringsInFile(string filename)
{
  // 1. Объявить дополнительные переменные
  int count;
  string* lines = nullptr;
  string s;

  // 2. Считать строки из файла
  count = GetStringsFromFileS(filename, &lines);

  // 3. Проверка, считались ли строки корректно
  if (count <= 0) return false;

  // 4. Реверсирование массива lines
  for (int i = 0; i < count / 2; i++)
  {
    s = lines[i];
    lines[i] = lines[count - i - 1];
    lines[count - i - 1] = s;
  }

  // 5. Запись массива lines в файл
  bool res = SetStringsToFileS(filename, lines, count);

  // 6. Освободить память, выделенную для массива lines
  if (lines != nullptr) delete[] lines;

  // 7. Вернуть результат
  return res;
}

Использование ReverseStringsInFile () может быть, например, следующим

void main()
{
  // Реверсирование строк файла
  string filename = "TextFile3.txt"; // исходный файл

  if (ReverseStringsInFile(filename))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

  ⇑

11. Функция SortStringsInFile(). Сортировка строк в файле

В примере демонстрируется сортировка строк файла с помощью метода вставки. Предварительно строки копируются в массив и там сортируются. Затем, отсортированный массив строк обратно записывается в файл.

#include <iostream>
using namespace std;

// модуль <fstream> необходим для работы с файлами
#include <fstream>

// Функция сортировки строк в файле методом вставки.
// Параметры:
// - filename - имя файла.
// Если сортировка произошла корректно, то функция возвращает true.
bool SortStringsInFile(string filename)
{
  // 1. Объявить дополнительные переменные
  int count;
  string* lines = nullptr;
  string s;

  // 2. Считать строки из файла в масив lines
  count = GetStringsFromFileS(filename, &lines); // функция описана в п.4

  // 3. Проверка, корректно ли считались строки
  if (count < 0) return false;

  // 4. Сортировка строк в файле по возрастанию методом вставки
  for (int i = 0; i < count - 1; i++)
    for (int j = i; j >= 0; j--)
      if (lines[j] > lines[j + 1])
      {
        s = lines[j];
        lines[j] = lines[j + 1];
        lines[j + 1] = s;
      }

  // 5. Записать отсортированный массив lines в файл
  bool res = SetStringsToFileS(filename, lines, count);

  // 6. Освободить память, выделенную для массива lines
  if (lines != nullptr) delete[] lines;

  // 7. Вернуть результат
  return res;
}

Использование файла в функции main()

void main()
{
  // Сортировка строк в файле
  string filename = "TextFile4.txt"; // исходный файл

  if (SortStringsInFile(filename))
  {
    cout << "OK!!!";
  }
  else
  {
    cout << "Error";
  }
}

  ⇑


Связанные темы

  ⇑