C++. Приклади роботи із текстовими файлами. Модифікація файлів. Сортування даних у файлах. Конвертування даних файлу до списку
Приклади роботи із текстовими файлами. Модифікація файлів. Сортування даних у файлах. Конвертування даних файлу в масив
У цій темі наводяться приклади вирішення відомих завдань, що виникають під час обробки текстових файлів мовою C++. Пропрацювавши рішення цих завдань, ви навчитеся обробляти масиви малих даних, керувати розподілом пам'яті і використовувати засоби C++ для роботи з текстовими файлами.
Пошук на інших ресурсах:
1. Як визначити, що дані у текстовому файлі закінчились?
Для визначення кінця файлу використовується функція eof(). Функція повертає результат типу bool. Прототип функції наступний
тут 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() повертає кількість рядків у текстовому файлі.
Використання функції GetStringsFromFileC() може бути, наприклад, таким:
void main() < // Виведення списку рядків файлу на екран // 1. Оголосити внутрішні змінні int count; // кількість рядків char** lines = nullptr; // Список рядків типу char* //2.Отримати список рядків типу char** count = GetStringsFromFileC("TextFile1.txt", &lines); // 3. Перевірка, чи отримано список if (count < 0) < cout "Error" return; > // 3. Вивести список рядків на екран for (int i = 0; i < count; i++) < cout // 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція, яка отримує рядки файлу як списку типу 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 // вивести повідомлення про помилку 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" return; > // 3. Вивести список рядків на екран for (int i = 0; i < count; i++) < cout // 4. Звільнити пам'ять, виділену для масиву lines if (Lines! = nullptr) delete[] lines; >
Результат роботи програми (вміст файлу TextFile1.txt):
#include using namespace std; void main()5. Функція SetStringsToFileS(). Записати масив (список) рядків типу string у текстовий файл
Працюючи з текстовими файлами корисна функція SaveStringsToFileS() , яка записує масив рядків у файл. У наведених нижче прикладах ця функція буде використовуватися для запису модифікованого списку у файл.
#include using namespace std; // модуль необхідний для роботи з файлами #include // Функція, яка записує масив рядків типу 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 // 4. Останній рядок записати як є (без символу '\n') F // 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 return; > // записати в рядки певний текст lines[0] = "#include "; lines[1] = "using namespace std"; lines[2] = "void main()"; lines[3] = "; lines[4] = ">"; // 3. Записати рядки у файл TextFile2.txt if (SetStringsToFileS("TextFile2.txt", lines, 5)) cout "OK!" else cout "Error." // 4. Звільнити пам'ять, виділену для масиву lines if (Lines! = nullptr) delete[] lines; >
6. Функція ChangeStringInFileC(). Заміна рядка у текстовому файлі
У прикладі наведено функцію, яка замінює рядок у файлі в заданій позиції. Ця функція використовує такі функції:
- CountLinesInFile() - визначає кількість рядків у файлі (реалізована в п. 3);
- GetStringsFromFileC() - повертає рядки файлу у вигляді масиву (реалізована в п. 4). Рядки представлені типом char*.
Ця функція не використовує додатковий файл для копіювання. Функція отримує рядки файлу у вигляді списку lines, які є копією файлу. Потім відбувається запис цих рядків у цей файл за винятком рядка, яку потрібно замінити у файлі. У позиції заміни записується вхідний рядок заміни. Таким чином відбувається заміна рядка у текстовому файлі.
#include using namespace std; // модуль необхідний роботи з файлами #include // Функція, що замінює рядок у файлі. // Параметри: // - 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 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 // вивести рядок у файл // 6. Запис рядка з позиції position F // тут пишеться рядок str // 7. Запис рядків після позиції position for (int i = position + 1; i < count – 1; i++) F // 8. Записати останній рядок без символу '\n' F // 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція, що видаляє рядок у файлі за заданим номером, // Номер рядка починається з 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція, яка вставляє вказаний рядок у вказану позицію файлу. // Параметри функції: // - 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 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція, яка змінює місцями два рядки у зазначених позиціях. // Параметри: // - 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція, яка звертає рядки файлу у зворотному порядку. // Параметри: // - filename - ім'я файлу. // Якщо операція відбулася успішно, функція повертає true. bool ReverseStringsInFile(string filename) < // 1. Оголосити додаткові змінні int count; string* lines = nullptr; string s; // 2. Вважати рядки з файлу count = GetStringsFromFileS(filename, &lines); // 3. Перевірка, чи вважалися рядки коректно if (count 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 using namespace std; // модуль необхідний роботи з файлами #include // Функція сортування рядків у файлі шляхом вставки. // Параметри: // - 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"; > >
Пов'язані теми
Як прочитати один рядок із файлу з ++?
Працюючи з текстовими файлами гразо простіше працювати з даними не як із окремими символами, бо як з рядками з допомогою функцій fgets() і fputs() .
Запис текстового файлу
Функція fputs() записує файл рядок, тобто набір символів, який завершується символом '\0'. Вона має наступний прототип:
int fputs(const char *s, FILE *stream);
Перший параметр функції - рядок, що записується, а другий - покажчик на файловий потік. Як результат функція повертає невід'ємне ціле число. При помилках у процесі запису повертається значення EOF.
Під час запису рядка нульовий символ '\0' у файл не записується.
Наприклад, запишемо рядок у файл:
#include int main(void) < // рядок для запису char * message = "Hello METANIT.COM!\nAn apple a day keeps the doctor away"; // файл для запису char*filename="data.txt"; // запис у файл FILE * fp = fopen (filename, "w"); if(fp) < // записуємо рядок fputs(message, fp); fclose(fp); printf("File has been written\n"); >>
Запис досить простий: відкриваємо файл на запис і за допомогою виклику fputs(message, fp) записуємо у файл шуканий рядок.
Читання текстового файлу
Функція fgets() зчитує з файлу один рядок і має наступний прототип:
char * fgets (char * s, int n, FILE * sream);
Перший параметр char *s представляє рядок, в який здійснюється зчитування. Другий параметр - число n вказує скільки символів необхідно зчитувати. І третій параметр є файловий потік, з якого проводиться зчитування.
Під час виклику функція зчитує з файлу трохи більше n-1 символів.Функція припиняє читання, коли прочитає n-1 символи або зустріне символ перенесення рядка \n. Усі лічені символи записуються в рядок s, зокрема символ \n. Також кінець кожного рядка дописується нульовий символ '\0'.
При успішному завершенні функція повертає покажчик s, а при помилці або досягненні кінця файлу повертається значення NULL.
Вважаємо дані з вищезаписаного файлу "data.txt":
#include int main(void) < // файл читання char * filename = "data.txt"; // буфер для зчитування даних із файлу char buffer[256]; // Читання з файлу FILE * fp = fopen (filename, "r"); if(fp) < // поки не дійдемо до кінця, зчитуємо по 256 байт while((fgets(buffer, 256, fp))!=NULL) < printf("%s", buffer); > fclose (fp); > >
Тут відкриваємо файл на читання і в циклі зчитуємо з файлу по 256 символів і виводимо їх на консоль за допомогою виклику fgets (cc, 256, fp). Коли дані у файлі закінчаться, функція поверне NULL, і відбудеться вихід із циклу.
Копіювання файлів
Інший приклад роботи з текстовими файлами - копіювання вмісту з одного файлу до іншого:
#include int main(void) < char * filename1 = "data1.txt"; char * filename2 = "data2.txt"; char buffer [256]; FILE * f1 = fopen (filename1, "r"); // файл для читання FILE * f2 = fopen (filename2, "w"); // файл запис if(!f1 || !f2) < printf("Error occured while opening file\n"); >else < // поки не дійдемо до кінця, зчитуємо по 256 байт із файлу f1 while((fgets(buffer, 256, f1))!=NULL) < // записуємо рядок у файл f2 fputs(buffer, f2); printf("%s", buffer); >> fclose(f1); fclose(f2); return 0; >