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";
  }
}

 


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