Робота з даними з файлу c. Робота з текстовими файлами. Читання з виконуваного файлу і запис в нього
Робота файлового введення / виводу в C ++ майже аналогічна роботі звичайних (але з невеликими нюансами).
Класи файлового введення / виводу
є три основні класи файлового введення / виводу в C ++:
ofstream(Є дочірнім класу);
fstream(Є дочірнім класу iostream).
За допомогою цих класів можна виконувати односпрямований файловий ввід, односпрямований файловий висновок і двонаправлений файловий ввід / вивід. Для їх використання потрібно всього лише підключити fstream.
На відміну від потоків cout, cin, cerr і clog, які відразу ж можна використовувати, файлові потоки повинні бути явно встановлені програмістом. Тобто, щоб відкрити файл для читання і / або запису, потрібно створити об'єкт відповідного класу файлового введення / виводу, вказавши ім'я файлу в якості параметра. Потім, за допомогою операторів вставки (<<) или извлечения (>>), Можна записувати дані в файл або читати вміст файлу. Після цього фінал - потрібно закрити файл: явно викликати метод close ()або просто дозволити файлової змінної введення / виведення вийти з області видимості (файлового класу введення / виведення закриє цей файл автоматично замість нас).
файловий висновок
Для запису в файл використовується клас ofstream. наприклад:
#include
#include #include #include int main () using namespace std; // ofstream використовується для запису даних в файл // Створюємо файл SomeText.txt ofstream outf ( "SomeText.txt"); // Якщо ми не можемо відкрити цей файл для запису даних в нього if (! outf) // Те виводимо повідомлення про помилку і виконуємо exit () cerr<< << endl ; exit (1); // Записуємо в файл наступні два рядки outf<< "See line #1!" << endl ; outf<< "See line #2!" << endl ; return 0; // Коли outf вийде з зони видимості, то деструктор класу ofstream автоматично закриє наш файл |
Якщо ви загляньте в каталог вашого проекту ( ПКМ по вкладці з назвою вашего.cpp файлу в Visual Studio > «Відкрити що містить папку»), То побачите файл з ім'ям SomeText.txt, в якому знаходяться такі рядки:
See line # 1!
See line # 2!
Зверніть увагу, ми також можемо використовувати метод put ()для запису одного символу в файл.
файловий ввід
#include
#include #include #include #include int main () using namespace std; // ifstream використовується для читання вмісту файлу // Якщо ми не можемо відкрити цей файл для читання його вмісту if (! inf) // Те виводимо наступне повідомлення про помилку і виконуємо exit () cerr<< << endl ; exit (1); // Поки є дані, які ми можемо прочитати while (inf) // Те перемещаем ці дані в рядок, яку потім виводимо на екран string strInput; inf >> strInput; cout<< strInput << endl ; return 0; // Коли inf вийде з зони видимості, то деструктор класу ifstream автоматично закриє наш файл |
See
line
#1!
See
line
#2!
Хм, це не зовсім те, що ми хотіли. Як ми вже знаємо з попередніх уроків, оператор вилучення працює з «відформатовані даними», тобто він ігнорує всі прогалини, символи табуляції і символ нового рядка. Щоб прочитати весь вміст як є, без його розбивки на частині (як в прикладі вище), нам потрібно використовувати метод getline ():
#include
#include #include #include #include int main () using namespace std; // ifstream використовується для читання вмісту файлів ifstream inf ( "SomeText.txt"); // Якщо ми не можемо відкрити файл для читання його вмісту if (! inf) // Те виводимо наступне повідомлення про помилку і виконуємо exit () cerr<< "Uh oh, SomeText.txt could not be opened for reading!"<< endl ; exit (1); while (inf) string strInput; getline (inf, strInput); cout<< strInput << endl ; return 0; // Коли inf вийде з зони видимості, то деструктор класу ifstream автоматично закриє наш файл |
Результат виконання програми вище:
буферізованние висновок
Висновок в C ++ може бути буферизованного. Це означає, що все, що виводиться в файловий потік, не може відразу ж бути записаним на диск (в конкретний файл). Це зроблено, в першу чергу, з міркувань продуктивності. Коли дані буфера записуються на диск, то це називається очищенням буфера. Одним із способів очищення буфера є закриття файлу. У такому випадку весь вміст буфера буде переміщено на диск, а потім файл буде закритий.
Буферизація виводу зазвичай не є проблемою, але, при певних обставинах, вона може викликати проблеми у необережних новачків. Наприклад, коли в буфері зберігаються дані і програма передчасно завершує своє виконання (або в результаті збою, або шляхом виклику). У таких випадках деструктори класів файлового введення / виводу не виконуються, файли ніколи не закриваються, буфери не очищається і наші дані губляться назавжди. Ось чому гарною ідеєю є явне закриття всіх відкритих файлів перед викликом exit ().
Також буфер можна очистити вручну, використовуючи метод ostream :: flush ()або відправивши std :: flushв вихідний потік. Будь-який з цих способів може бути корисний для забезпечення негайної запису вмісту буфера на диск в разі збою програми.
цікавий нюанс: Оскільки std :: endl; також очищає вихідний потік, то його надмірне використання (що приводить до непотрібних очисткам буфера) може вплинути на продуктивність програми (так як очищення буфера в деяких випадках може бути витратною операцією). З цієї причини програмісти, які піклуються про продуктивність свого коду, часто використовують \ n замість std :: endl для вставки символу нового рядка у вихідний потік, щоб уникнути непотрібної очищення буфера.
Режими відкриття файлів
Що станеться, якщо ми спробуємо записати дані в уже існуючий файл? Повторний запуск програми вище (найперша) показує, що вихідний файл повністю перезаписується при повторному запуску програми. А що, якщо нам потрібно додати дані в кінець файлу? Виявляється, файлового потоку приймають необов'язковий другий параметр, який дозволяє вказати програмісту спосіб відкриття файлу. Як цього параметра можна передавати такі прапори(Які знаходяться в класі ios):
app- відкриває файл в режимі додавання;
ate- переходить в кінець файлу перед читанням / записом;
binary- відкриває файл в бінарному режимі (замість текстового режиму);
in- відкриває файл в режимі читання (за замовчуванням для ifstream);
out- відкриває файл в режимі запису (за замовчуванням для ofstream);
trunc- видаляє файл, якщо він вже існує.
Можна вказати відразу кілька прапорів шляхом використання.
ifstream за замовчуванням працює в режимі ios :: in;
ofstream за замовчуванням працює в режимі ios :: out;
fstream за замовчуванням працює в режимі ios :: in АБО ios :: out, що означає, що ви можете виконувати як читання вмісту файлу, так і запис даних в файл.
Тепер давайте напишемо програму, яка додасть два рядки в раніше створений файл SomeText.txt:
#include
#include #include #include int main () using namespace std; // Передаємо прапор ios: app, щоб повідомити fstream, що ми збираємося додати свої дані до вже існуючих даних файлу, // ми не збираємося перезаписувати файл. Нам не потрібно передавати прапор ios :: out, // оскільки ofstream за замовчуванням працює в режимі ios :: out ofstream outf ( "SomeText.txt", ios :: app); // Якщо ми не можемо відкрити файл для запису даних if (! outf) // Те виводимо наступне повідомлення про помилку і виконуємо exit () cerr<< "Uh oh, SomeText.txt could not be opened for writing!"<< endl ; exit (1); |
Файли дозволяють користувачеві зчитувати великі обсяги даних безпосередньо з диска, не вводячи їх з клавіатури. Існують два основних типи файлів: текстові та двійкові.
текстовиминазиваються файли, що складаються з будь-яких символів. Вони організовуються по рядках, кожна з яких закінчується символом « кінця рядка ». Кінець самого файлу позначається символом « кінця файлу ». При запису інформації в текстовий файл, переглянути який можна за допомогою будь-якого текстового редактора, всі дані перетворюються до символьного типу і зберігаються в символьному вигляді.
В довічнихфайлах інформація зчитується і записується у вигляді блоків певного розміру, в яких можуть зберігатися дані будь-якого виду і структури.
Для роботи з файлами використовуються спеціальні типи даних, звані потоками.потік ifstreamслужить для роботи з файлами в режимі читання, а ofstreamв режимі запису. Для роботи з файлами в режимі як записи, так і читання служить потік fstream.
У програмах на C ++ при роботі з текстовими файлами необхідно підключати бібліотеки iostreamі fstream.
Для того щоб записувати дані в текстовий файл, необхідно:
- описати змінну типу ofstream.
- open.
- вивести інформацію в файл.
- обов'язково закрити файл.
Для зчитування даних з текстового файлу, необхідно:
- описати змінну типу ifstream.
- відкрити файл за допомогою функції open.
- зчитати інформацію з файлу, при зчитуванні кожної порції даних необхідно перевіряти, чи досягнуто кінець файлу.
- закрити файл.
Запис інформації в текстовий файл
Як було сказано раніше, для того щоб почати працювати з текстовим файлом, необхідно описати змінну типу ofstream. Наприклад, так:
ofstream F;
Буде створена змінна Fдля запису інформації в файл. На наступних етапі файл необхідно відкрити для запису. У загальному випадку оператор відкриття потоку матиме вигляд:
F.open(«File», mode);
тут F- змінна, описана як ofstream, file- повне ім'я файлу на диску, mode- режим роботи з відкриваються файлом. Зверніть увагу на те, що при вказівці повного імені файлу потрібно ставити подвійний слеш. Для звернення, наприклад до файлу accounts.txt,що знаходиться в папці sites на диску D, В програмі необхідно вказати: D: \\ sites \\ accounts.txt.
Файл може бути відкритий в одному з наступних режимів:
- ios :: in- відкрити файл в режимі читання даних; режим є режимом за замовчуванням для потоків ifstream;
- ios :: out- відкрити файл в режимі запису даних (при цьому інформація про існуючий файлі знищується); режим є режимом за замовчуванням для потоків ofstream;
- ios :: app- відкрити файл в режимі запису даних в кінець файлу;
- ios :: ate- пересунутися в кінець вже відкритого файлу;
- ios :: trunc- очистити файл, це ж відбувається в режимі ios :: out;
- ios :: nocreate- не виконувати операцію відкриття файлу, якщо він не існує;
- ios :: noreplace- не відкривати існуючий файл.
Параметр mode може бути відсутнім, в цьому випадку файл відкривається в режимі за замовчуванням для даного потоку.
Після вдалого відкриття файлу (в будь-якому режимі) в змінної Fбуде зберігатися true, в іншому випадку false. Це дозволить перевірити коректність операції відкриття файлу.
Відкрити файл (як приклад візьмемо файл D: \\ sites \\ accounts.txt) В режимі запису можна одним з таких способів:
Після відкриття файлу в режимі запису буде створено порожній файл, в який можна буде записувати інформацію.
Якщо ви хочете відкрити існуючий файл в режимі дозаписи, то в якості режиму слід використовувати значення ios :: app.
Після відкриття файлу в режимі запису, в нього можна писати точно так же, як і на екран, тільки замість стандартного пристрою виведення coutнеобхідно вказати ім'я відкритого файлу.
Наприклад, для запису в потік Fзмінної a, Оператор виведення матиме вигляд:
Для послідовного виведення в потік Gзмінних b, c, dоператор виведення стане таким:
G<
Закриття потоку здійснюється за допомогою оператора:
F.close ();
Як приклад розглянемо наступну задачу.
завдання 1
Створити текстовий файл D: \\ sites\\accounts .txtі записати в нього nдійсних чисел.
Рішення
1 |
#include «stdafx.h» |
Читання інформації з текстового файлу
Для того щоб прочитати інформацію з текстового файлу, необхідно описати змінну типу ifstream. Після цього потрібно відкрити файл для читання за допомогою оператора open. Якщо змінну назвати F, То перші два оператора будуть такими:
Після відкриття файлу в режимі читання з нього можна зчитувати інформацію точно так же, як і з клавіатури, тільки замість cinпотрібно вказати ім'я потоку, з якого відбуватиметься читання даних.
Наприклад, для читання даних з потоку Fв змінну a, Оператор введення буде виглядати так:
F >> a;
Два числа в текстовому редакторі вважаються розділеними, якщо між ними є хоча б один із символів: пробіл, табуляція, символ кінця рядка. Добре, коли програмісту заздалегідь відомо, скільки і які значення зберігаються в текстовому файлі. Однак часто відомий лише тип значень, що зберігаються в файлі, при цьому їх кількість може бути різним. Для вирішення даної проблеми необхідно зчитувати значення з файлу по черзі, а перед кожним зчитуванням перевіряти, чи досягнуто кінець файлу. А допоможе зробити це функція F.eof (). тут F- ім'я потоку функція повертає логічне значення: trueабо false, В залежності від того чи досягнуто кінець файлу.
Отже, цикл для читання вмісту всього файлу можна записати так:
Для кращого засвоєння матеріалу розглянемо задачу.
завдання 2
У текстовому файлі D: \\ game \\ accounts.txt зберігаються речові числа, вивести їх на екран і обчислити їх кількість.
Рішення
1 |
#include «stdafx.h» |
На цьому щодо об'ємний урок по текстових файлів закінчений. У наступній статті будуть розглянуті методи маніпуляції, за допомогою яких в C ++ обробляються.
Останнє оновлення: 31.10.2015
Для роботи з каталогами в просторі імен System.IO призначені відразу два класи: Directory і DirectoryInfo.
клас Directory
Клас Directory надає ряд статичних методів для управління каталогами. Деякі з цих методів:
CreateDirectory (path): створює каталог за вказаною шляху path
Delete (path): видаляє каталог по зазначеному шляху path
Exists (path): визначає, чи існує каталог по зазначеному шляху path. Якщо існує, повертається true, якщо не існує, то false
GetDirectories (path): отримує список каталогів в каталозі path
GetFiles (path): отримує список файлів в каталозі path
Move (sourceDirName, destDirName): Переміщує каталог
GetParent (path): отримання батьківського каталогу
клас DirectoryInfo
Даний клас надає функціональність для створення, видалення, переміщення і інших операцій з каталогами. Багато в чому він схожий на Directory. Деякі з його властивостей і методів:
Create (): створює каталог
CreateSubdirectory (path): створює підкаталог за вказаною шляху path
Delete (): видаляє каталог
Властивість Exists: визначає, чи існує каталог
GetDirectories (): отримує список каталогів
GetFiles (): отримує список файлів
MoveTo (destDirName): переміщує каталог
Властивість Parent: отримання батьківського каталогу
Властивість Root: отримання кореневого каталогу
Подивимося на прикладах застосування цих класів
Отримання списку файлів і підкаталогів
string dirName = "C: \\"; if (Directory.Exists (dirName)) (Console.WriteLine ( "Підкаталоги:"); string dirs = Directory.GetDirectories (dirName); foreach (string s in dirs) (Console.WriteLine (s);) Console.WriteLine ( ); Console.WriteLine ( "Файли:"); string files = Directory.GetFiles (dirName); foreach (string s in files) (Console.WriteLine (s);))Зверніть увагу на використання слешів в іменах файлів. Або ми використовуємо подвійний слеш: "C: \\", або одинарний, але тоді перед усією дорогою ставимо знак @: @ "C: \ Program Files"
створення каталогу
string path = @ "C: \ SomeDir"; string subpath = @ "program \ avalon"; DirectoryInfo dirInfo = new DirectoryInfo (path); if (! dirInfo.Exists) (dirInfo.Create ();) dirInfo.CreateSubdirectory (subpath);Спочатку перевіряємо, а чи немає такої директорії, так як якщо вона існує, то її створити не можна буде, і додаток викине помилку. В результаті у нас вийде наступний шлях: "C: \ SomeDir \ program \ avalon"
Отримання інформації про каталог
string dirName = "C: \\ Program Files"; DirectoryInfo dirInfo = new DirectoryInfo (dirName); Console.WriteLine ($ "Назва каталогу: (dirInfo.Name)"); Console.WriteLine ($ "Повна назва каталогу: (dirInfo.FullName)"); Console.WriteLine ($ "Час створення каталогу: (dirInfo.CreationTime)"); Console.WriteLine ($ "Кореневий каталог: (dirInfo.Root)");видалення каталогу
Якщо ми просто застосуємо метод Delete до непорожній папці, в якій є якісь файли або підкаталоги, то додаток нам викине помилку. Тому нам треба передати в метод Delete додатковий параметр булевого типу, який вкаже, що папку треба видаляти з усім вмістом:
String dirName = @ "C: \ SomeFolder"; try (DirectoryInfo dirInfo = new DirectoryInfo (dirName); dirInfo.Delete (true); Console.WriteLine ( "Каталог видалений");) catch (Exception ex) (Console.WriteLine (ex.Message);)
String dirName = @ "C: \ SomeFolder"; Directory.Delete (dirName, true);
переміщення каталогу
string oldPath = @ "C: \ SomeFolder"; string newPath = @ "C: \ SomeDir"; DirectoryInfo dirInfo = new DirectoryInfo (oldPath); if (dirInfo.Exists && Directory.Exists (newPath) == false) (dirInfo.MoveTo (newPath);)При переміщенні треба враховувати, що новий каталог, в який ми хочемо перемісити весь вміст старого каталогу, не повинен існувати.
Для зручності користування інформація в запам'ятовуючих пристроях зберігається у вигляді файлів.
Файл - іменована область зовнішньої пам'яті, виділена для зберігання масиву даних. Дані, що містяться в файлах, мають найрізноманітніший характер: програми на алгоритмічній або машинному мовою; вихідні дані для роботи програм або результати виконання програм; довільні тексти; графічні зображення і т. п.
Каталог (папка, директорія) - іменована сукупність байтів на носії інформації, яка містить назву підкаталогів і файлів, використовується в файлової системі для спрощення організації файлів.
файлової системоюназивається функціональна частина операційної системи, що забезпечує виконання операцій над файлами. Прикладами файлових систем є FAT (FAT - File Allocation Table, таблиця розміщення файлів), NTFS, UDF (використовується на компакт-дисках).
Існують три основні версії FAT: FAT12, FAT16 і FAT32. Вони відрізняються розрядністю записів в дискової структурі, тобто кількістю біт, відведених для зберігання номера кластера. FAT12 застосовується в основному для дискет (до 4 кбайт), FAT16 - для дисків малого обсягу, FAT32 - для FLASH-накопичувачів великої місткості (до 32 Гбайт).
Розглянемо структуру файлової системи на прикладі FAT32.
Файлова структура FAT32
Пристрої зовнішньої пам'яті в системі FAT32 мають не байтовую, а блокову адресацію. Запис інформації в пристрій зовнішньої пам'яті здійснюється блоками або секторами.
Сектор - мінімальна адресується одиниця зберігання інформації на зовнішніх запам'ятовуючих пристроях. Як правило, розмір сектора фіксований і становить 512 байт. Для збільшення адресного простору пристроїв зовнішньої пам'яті сектора об'єднують в групи, які називаються кластерами.
Кластер - об'єднання декількох секторів, яке може розглядатися як самостійна одиниця, що володіє певними властивостями. Основною властивістю кластера є його розмір, вимірюваний в кількості секторів або кількості байт.
Файлова система FAT32 має наступну структуру.
Нумерація кластерів, використовуваних для запису файлів, ведеться з 2. Як правило, кластер №2 використовується кореневим каталогом, а починаючи з кластера №3 зберігається масив даних. Сектора, що використовуються для зберігання інформації, представленої вище кореневого каталогу, в кластери не об'єднує.
Мінімальний розмір файлу, який займає на диску, відповідає 1 кластеру.
Завантажувальний сектор починається наступною інформацією:
- EB 58 90 - безумовний перехід і сигнатура;
- 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
- 00 02 - кількість байт в секторі (зазвичай 512);
- 1 байт - кількість секторів в кластері;
- 2 байта - кількість резервних секторів.
Крім того, завантажувальний сектор містить наступну важливу інформацію:
- 0x10 (1 байт) - кількість таблиць FAT (зазвичай 2);
- 0x20 (4 байта) - кількість секторів на диску;
- 0x2С (4 байта) - номер кластера кореневого каталогу;
- 0x47 (11 байт) - мітка тому;
- 0x1FE (2 байта) - сигнатура завантажувального сектора (55 AA).
Сектор інформації файлової системи містить:
- 0x00 (4 байта) - сигнатура (52 52 61 41);
- 0x1E4 (4 байта) - сигнатура (72 72 41 61);
- 0x1E8 (4 байта) - кількість вільних кластерів, -1 якщо не відомо;
- 0x1EС (4 байта) - номер останнього записаного кластера;
- 0x1FE (2 байта) - сигнатура (55 AA).
Таблиця FAT містить інформацію про стан кожного кластера на диску. Молодші 2 байт таблиці FAT зберігають F8 FF FF 0F FF FF FF FF (що відповідає стану кластерів 0 і 1, фізично відсутніх). Далі стан кожного кластера містить номер кластера, в якому триває поточний файл або наступну інформацію:
- 00 00 00 00 - кластер вільний;
- FF FF FF 0F - кінець поточного файлу.
- 8 байт - ім'я файлу;
- 3 байта - розширення файлу;
Кореневої каталог містить набір 32-бітових записів інформації про кожен файл, що містять наступну інформацію:
У разі роботи з довгими іменами файлів (включаючи російські імена) кодування імені файлу проводиться в системі кодування UTF-16. При цього для кодування кожного символу відводиться 2 байти. При цьому ім'я файлу записується у вигляді такої структури:
- 1 байт послідовності;
- 10 байт містять молодші 5 символів імені файлу;
- 1 байт атрибут;
- 1 байт резервний;
- 1 байт - контрольна сума імені DOS;
- 12 байт містять молодші 3 символу імені файлу;
- 2 байта - номер першого кластера;
- інші символи довгого імені.
Робота з файлами в мові Сі
Для програміста відкритий файл представляється як послідовність зчитуються або записуються даних. При відкритті файлу з ним зв'язується потік вводу-виводу. Інформація, що виводиться інформація записується в потік, що вводиться, зчитується з потоку.
Коли потік відкривається для введення-виведення, він зв'язується зі стандартною структурою типу FILE, яка визначена в stdio.h. Структура FILE містить необхідну інформацію про файл.
Відкриття файлу здійснюється за допомогою функції fopen (), яка повертає покажчик на структуру типу FILE, який можна використовувати для подальших операцій з файлом.
FILE * fopen (name, type);
name - ім'я файлу (включаючи шлях),
type - покажчик на рядок символів, що визначають спосіб доступу до файлу:
- "R" - відкрити файл для читання (файл повинен існувати);
- "W" - відкрити порожній файл для запису; якщо файл існує, то його вміст втрачається;
- "A" - відкрити файл для запису в кінець (для додавання); файл створюється, якщо він не існує;
- "R +" - відкрити файл для читання і запису (файл повинен існувати);
- "W +" - відкрити порожній файл для читання і запису; якщо файл існує, то його вміст втрачається;
- "A +" - відкрити файл для читання і доповнення, якщо файл не існує, то він створюється.
Значення, що повертається - покажчик на відкритий потік. Якщо виявлена помилка, то повертається значення NULL.
Функція fclose () закриває потік або потоки, пов'язані з відкритими за допомогою функції fopen () файлами. Закривається потік визначається аргументом функції fclose ().
Значення, що повертається: значення 0, якщо потік успішно закритий; константа EOF, якщо сталася помилка.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include
int main () (
FILE * fp;
char name = "my.txt";
if ((fp = fopen (name, "r")) == NULL)
{
printf ( "Неможливо відкрити файл");
getchar ();
return 0;
}
// відкрити файл вдалося
... // необхідні дії над даними
fclose (fp);
getchar ();
return 0;
}
Читання символу з файлу:
char fgetc (потік);
Аргументом функції є покажчик на потік типу FILE. Функція повертає код ліченого символу. Якщо досягнуто кінця файлу або виникла помилка, повертається константа EOF.
Запис символу в файл:
fputc (символ, потік);
Аргументами функції є символ і покажчик на потік типу FILE. Функція повертає код ліченого символу.
Функції fscanf () і fprintf () аналогічні функціям scanf () і printf (), але працюють з файлами даних, і мають перший аргумент - покажчик на файл.
fscanf (потік, "ФорматВвода", аргументи);