Примеры работы с текстовыми файлами. Модификация файлов. Сортировка данных в файлах. Конвертирование данных файла в массив
В данной теме наводятся примеры решения известных задач, возникающих при обработке текстовых файлов на языке C++. Проработав решения этих задач, вы научитесь обрабатывать массивы строчных данных, управлять распределением памяти а также использовать средства C++ для работы с текстовыми файлами.
Содержание
-
- 1. Как определить, что данные в текстовом файле закончились?
- 2. Функция CountLinesInFile(). Подсчет количества строк в текстовом файле
- 3. Функция GetStringsFromFileC(). Получить массив (список) строк типа char* из текстового файла
- 4. Функция GetStringsFromFileS(). Получить массив строк типа string из текстового файла
- 5. Функция SetStringsToFileS(). Записать массив (список) строк типа string в текстовый файл
- 6. Функция ChangeStringInFileC(). Замена строки в текстовом файле
- 7. Функция RemoveStringFromFileByIndex(). Удаление строки из файла по его номеру
- 8. Функция InsertStringToFile(). Вставка строки в заданную позицию в файле
- 9. Функция SwapStringsInFile(). Обмен местами двух строк в файле
- 10. Функция ReverseStringsInFile(). Реверсирования строк файла (перестановка строк файла в обратном порядке)
- 11. Функция SortStringsInFile(). Сортировка строк в файле
- Связанные темы
Поиск на других ресурсах:
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() следующий:
- Прочитать строки файла в список.
- Поменять строки в списке.
- Записать измененный список обратно в файл.
Текст функции обмена строк следующий.
#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"; } }
⇑
Связанные темы
- Файловая система. Принципы работы файловой системы C++. Примеры. Открытие/закрытие файла
- Примеры использования средств C++ для работы с файлами
⇑