Приклади роботи з текстовими файлами. Модифікація файлів. Сортування даних у файлах. Конвертування даних файлу в масив
У даній темі наведено приклади рішення поширених задач, що виникають при обробці текстових файлів на мові 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(). Обмін місцями двох рядків у файлі
Скорочений алгоритм функції SwapStringsInFiles() наступний:
- Зчитати рядки файлу в список.
- Поміняти рядки в списку.
- Записати змінений список знову в файл.
Текст функції обміну рядків наведено нижче.
#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++ для роботи з файлами
⇑