Работата на файлов I/O в C++ е почти същата като тази на обикновен I/O (но с няколко нюанса).

Файлови I/O класове

Има три основни файлови I/O класа в C++:

извън течението(е дете на класа);

fstream(е дете на класа iostream).

С тези класове можете да изпълнявате еднопосочен файлов вход, еднопосочен файлов изход и двупосочен файлов I/O. За да ги използвате, просто трябва да свържете fstream.

За разлика от потоците cout, cin, cerr и clog, които могат да се използват веднага, файловите потоци трябва да бъдат изрично зададени от програмиста. Тоест, за да отворите файл за четене и/или запис, трябва да създадете обект от съответния файлов I/O клас, като посочите името на файла като параметър. След това, използвайки операторите за вмъкване (<<) или извлечения (>>), можете да запишете данни във файл или да прочетете съдържанието на файл. След това финалът - трябва да затворите файла: изрично извикване метод close().или просто оставете файловата I/O променлива да излезе извън обхвата (файловият I/O клас ще затвори този файл автоматично за нас).

Изход на файл

Класът ofstream се използва за запис във файл. Например:

#включи #включи #включи // за използване на exit() int main() (използване на пространство от имена std; // ofstream се използва за запис на данни във файл // Създаване на файл SomeText.txt ofstream outf("SomeText.txt"); // Ако можем Не отваряйте този файл, за да запишете данни в if (!outf) ( // След това отпечатайте съобщение за грешка и изпълнете exit() cerr<< "Uh oh, SomeText.txt could not be opened for writing!" << endl; exit(1); } // Записываем в файл следующие две строчки outf << "See line #1!" << endl; outf << "See line #2!" << endl; return 0; // Когда outf выйдет из области видимости, то деструктор класса ofstream автоматически закроет наш файл }

#включи

#включи

#включи // за използване на exit()

int main()

използване на пространство от имена std;

// ofstream се използва за запис на данни във файл

// Създаване на файл SomeText.txt

ofstream outf("SomeText.txt" ) ;

// Ако не можем да отворим този файл, за да запишем данни в него

ако (!outf)

// След това отпечатайте съобщение за грешка и изпълнете exit()

cerr<< << endl ;

изход(1);

// Напишете следните два реда във файла

извън<< "See line #1!" << endl ;

извън<< "See line #2!" << endl ;

връщане 0;

// Когато outf излезе извън обхвата, деструкторът на класа ofstream автоматично ще затвори нашия файл

Ако погледнете в директорията на вашия проект ( RMB в раздела с името на вашия .cpp файл във Visual Studio > "Отвори съдържащата папка"), ще видите файл, наречен SomeText.txt, който съдържа следните редове:

Вижте ред #1!
Вижте ред #2!

Моля, имайте предвид, че можем също да използваме put() методза да запишете един символ във файл.

Въвеждане на файл

#включи #включи #включи #включи // за използване на exit() int main() (използване на namespace std; // ifstream се използва за четене на съдържанието на файла // Опитайте се да прочетете съдържанието на файла SomeText.txt ifstream inf("SomeText.txt") ; // Ако не можем да отворим този файл, за да прочетем съдържанието му if (!inf) ( cerr<< "Uh oh, SomeText.txt could not be opened for reading!" << endl; exit(1); } // Пока есть данные, которые мы можем прочитать while (inf) { // То перемещаем эти данные в строку, которую затем выводим на экран string strInput; inf >> strInput; cout<< strInput << endl; } return 0; }

#включи

#включи

#включи

#включи // за използване на exit()

int main()

използване на пространство от имена std;

// ifstream се използва за четене на съдържанието на файла

// Ако не можем да отворим този файл, за да прочетем съдържанието му

ако (!inf)

// След това отпечатайте следното съобщение за грешка и изпълнете exit()

cerr<< << endl ;

изход(1);

// Докато има данни, които можем да четем

докато (inf)

// След това преместваме тези данни в низ, който след това показваме на екрана

низ strInput;

inf >> strInput;

cout<< strInput << endl ;

връщане 0;

// Когато inf излезе извън обхвата, деструкторът на класа ifstream автоматично ще затвори нашия файл

Вижте
линия
#1!
Вижте
линия
#2!

Хм, това не е точно това, което искахме. Както вече знаем от предишните уроци, операторът за извличане работи с "форматирани данни", т.е. той игнорира всички интервали, раздели и нови редове. За да прочетем цялото съдържание такова, каквото е, без да го разделяме на части (както в примера по-горе), трябва да използваме getline() метод:

#включи #включи #включи #включи // за използване на exit() int main() (използване на пространство от имена std; // ifstream се използва за четене на съдържанието на файлове // Ще се опитаме да прочетем съдържанието на файла SomeText.txt 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 автоматически закроет наш файл }

#включи

#включи

#включи

#включи // за използване на exit()

int main()

използване на пространство от имена std;

// ifstream се използва за четене на съдържанието на файлове

ifstream inf("SomeText.txt" ) ;

// Ако не можем да отворим файла, за да прочетем съдържанието му

ако (!inf)

// След това отпечатайте следното съобщение за грешка и изпълнете exit()

cerr<< „О, SomeText.txt не можа да бъде отворен за четене!“<< endl ;

изход(1);

докато (inf)

низ strInput;

getline (inf, strInput);

cout<< strInput << endl ;

връщане 0;

// Когато inf излезе извън обхвата, деструкторът на класа ifstream автоматично ще затвори нашия файл

Резултатът от изпълнението на горната програма е:

буфериран изход

Изходът в C++ може да бъде буфериран. Това означава, че всичко, което се извежда към файловия поток, не може веднага да бъде записано на диск (в конкретен файл). Това се прави предимно от съображения за производителност. Когато буферните данни се записват на диск, това се извиква изчистване на буфера. Един от начините за изчистване на буфера е да затворите файла. В този случай цялото съдържание на буфера ще бъде преместено на диска и след това файлът ще бъде затворен.

Изходното буфериране обикновено не е проблем, но при определени обстоятелства може да причини проблеми на непредпазливите начинаещи. Например, когато данните се съхраняват в буфера и програмата прекрати изпълнението си преждевременно (или в резултат на срив, или чрез извикване). В такива случаи файловите I/O клас деструктори не се изпълняват, файловете никога не се затварят, буферите не се изчистват и нашите данни се губят завинаги. Ето защо е добра идея изрично да затворите всички отворени файлове, преди да извикате exit().

Можете също да изчистите буфера ръчно, като използвате ostream::flush() методили чрез изпращане std::flushкъм изходния поток. Всеки от тези методи може да бъде полезен, за да се гарантира, че съдържанието на буфера незабавно се записва на диска в случай на срив на програмата.

Интересен нюанс: Защото std::endl; също така изчиства изходящия поток, след което прекомерното му използване (което води до ненужни промивания на буфери) може да повлияе на производителността на програмата (тъй като промиването на буфери може да бъде скъпо в някои случаи). Поради тази причина програмистите, загрижени за производителността, често използват \n вместо std::endl, за да вмъкнат символ за нов ред в изходния поток, за да избегнат ненужното изчистване на буфера.

Режими за отваряне на файлове

Какво се случва, ако се опитаме да запишем данни във вече съществуващ файл? Повторното стартиране на програмата по-горе (първата) показва, че оригиналният файл е напълно презаписан, когато стартирате програмата отново. Но какво ще стане, ако трябва да добавим данни в края на файла? Оказва се, че файловият поток приема незадължителен втори параметър, който ви позволява да кажете на програмиста как да отвори файла. Като този параметър можете да преминете следващи знамена(които са в класа на ios):

ап- отваря файла в режим на добавяне;

яде- отива в края на файла преди четене/запис;

двоичен- отваря файла в двоичен режим (вместо в текстов режим);

в- отваря файл в режим на четене (по подразбиране за ifstream);

навън- отваря файл в режим на запис (по подразбиране за ofstream);

trunc- изтрива файла, ако вече съществува.

Можете да посочите няколко флага наведнъж, като използвате .

ifstream работи в режим ios::in по подразбиране;

ofstream работи в режим ios::out по подразбиране;

fstream работи в режим ios::in ИЛИ ios::out по подразбиране, което означава, че можете или да четете съдържанието на файл, или да записвате данни във файл.

Сега нека напишем програма, която ще добави два реда към предварително създадения файл SomeText.txt:

#включи #включи // за използване на exit() #include int main() (използване на namespace std; // Подаване на флага ios:app, за да кажем на fstream, че ще добавим нашите данни към вече съществуващите данни на файла, // няма да презапишем файла. Ние не трябва да предаде флага ios::out, // тъй като ofstream по подразбиране е ios::out ofstream outf("SomeText.txt", ios::app); // Ако не можем да отворим файл, за да напишем данни, ако (!outf) ( // След това изведете следното съобщение за грешка и exit() cerr<< "Uh oh, SomeText.txt could not be opened for writing!" << endl; exit(1); } outf << "See line #3!" << endl; outf << "See line #4!" << endl; return 0; // Когда outf выйдет из области видимости, то деструктор класса ofstream автоматически закроет наш файл }

#включи

#включи // за използване на exit()

#включи

int main()

използване на пространство от имена std;

// Предаване на флага ios:app, за да кажем на fstream, че ще добавим нашите данни към вече съществуващите данни на файла,

// няма да презапишем файла. Не е необходимо да предаваме ios::out флага,

// защото ofstream по подразбиране е в режим ios::out

ofstream outf("SomeText.txt" , ios::app ) ;

// Ако не можем да отворим файла, за да напишем данни

ако (!outf)

// След това отпечатайте следното съобщение за грешка и изпълнете exit()

cerr<< „О, SomeText.txt не можа да бъде отворен за писане!“<< endl ;

изход(1);

Входно-изходният механизъм, разработен от , не съответства на общоприетия стил на обектно-ориентирано програмиране днес, освен това той активно използва операции с указатели, които се считат за потенциално опасни в съвременните защитени среди за изпълнение на код. Алтернатива за разработване на приложения е стандартният I/O клас механизъм, осигурен от езиковия стандарт C++.

Отваряне на файлове

Най-често използваните класове са ifstream за четене, ofstream за писане и fstream за модифициране на файлове.

Всички I/O класове с нишки са индиректно извлечени от общия предшественик ios, като напълно наследяват неговата функционалност. Например членът с изброени данни open_mode указва режима на отваряне на файла, който е дефиниран както следва:

Enum open_mode (приложение, двоичен, in, out, trunc, ate);

По-долу са възможните стойности на флаговете и тяхното предназначение.

Например, за да отворите файл, наречен test.txt за четене на двоични данни, трябва да напишете:

ifstream файл; file.open("test.txt", ios::in | ios::binary);

Логическият оператор ИЛИ (|) ви позволява да съставите режим с произволна комбинация от флагове. За да не презапишете случайно съществуващ файл със същото име, когато отваряте файл чрез писане, трябва да използвате следния формуляр:

файл извън потока; file.open("test.txt", ios::out | ios::app);

Предполага се, че подходящият заглавен файл е свързан към проекта:

#включи

За да проверите дали файлът е отворен успешно, можете да използвате конструкцията

If (!file) ( // Обработване на грешка при отваряне на файл )

Оператори за включване и извличане

Отменено в класовете за обработка на файлове включи оператор (<<) записывает данные в файловый поток. Как только вы открыли файл для записи, можно записывать в него текстовую строку целиком:

файл<< "Это строка текста";

Можете също да напишете текстов низ на части:

файл<< "Это " << "строка " << "текста";

Операторът endl завършва входния ред с връщане на каретка:

файл<< "Это строка текста" << endl;

С помощта на оператора за включване е лесно да запишете стойностите на променливи или елементи на масив във файл:

Ofstream файл ("Temp.txt"); char buff = "Текстовият масив съдържа променливи"; int vx = 100; float pi = 3,14159; файл<< buff << endl << vx << endl << pi << endl;

В резултат на изпълнението на кода се генерират три реда от текстовия файл Temp.txt:

Текстовият масив съдържа променливи 100 3.14159

Имайте предвид, че числовите стойности се записват във файла като текстови низове, а не като двоични стойности.

оператор за извличане(>>) прави обратното. Изглежда, че за да извлечете знаците от файла Temp.txt, написан по-рано, трябва да напишете код като следния:

файл ifstream("Temp.txt"); овъглен баф; intvx; floatpi; файл >> buff >> vx >> pi;

Операторът за извличане обаче ще спре при първия разделител (интервал, табулация или нов ред), който срещне. По този начин, когато анализирате изречението „Текстовият масив съдържа променливи“, само думата „Текст“ ще бъде записана в buff на масива, интервалът се игнорира и думата „масив“ ще стане стойността на целочислената променлива vx и кода изпълнението ще "обърка" с неизбежно нарушаване на структурата на данните. След това, когато обсъждаме класа ifstream, ще покажем как правилно да организираме четенето на файла от предишния пример.

клас ifstream: четене на файлове

Както подсказва името, класът ifstream е предназначен за въвеждане на файлов поток. Основните методи на класа са изброени по-долу. Повечето от тях са наследени от класа istream и са претоварени с родителска функционалност. Например функцията get, в зависимост от параметъра за извикване, може да прочете не само един символ, но и блок от знаци.

Сега е ясно как трябва да модифицирате предишния пример, така че използването на оператора за извличане на данни да даде очаквания резултат:

файл ifstream("Temp.txt"); овъглен баф; intvx; floatpi; file.getline(buff, sizeof(buff)); файл >> vx >> pi:

Методът getline ще прочете първия ред на файла до края, а операторът >> ще присвои стойности на променливи.

Следващият пример показва добавяне на данни към текстов файл и след това четене на целия файл. Цикълът while (1) се използва вместо while(!file2.eof()) поради причини, обсъдени в .

#включи #включи използване на пространство от имена std; int main() ( ofstream file; file.open("test.txt",ios::out|ios::app); if (!file) ( cout<< "File error - can"t open to write data!"; cin.sync(); cin.get(); return 1; } for (int i=0; i<10; i++) file << i << endl; file.close(); ifstream file2; file2.open("test.txt", ios::in); if (!file2) { cout << "File error - can"t open to read data!"; cin.sync(); cin.get(); return 2; } int a,k=0; while (1) { file2 >>a; if (file2.eof()) break; cout<< a << " "; k++; } cout << endl << "K=" << k << endl; file2.close(); cin.sync(); cin.get(); return 0; }

Следващият пример преминава през четене на редове от файла test.txt и показването им на конзолата.

#включи #включи използване на пространство от имена std; int main() ( ifstream файл; // създаване на обектен файл на поток file.open("test.txt"); // отваряне на файл за четене if (!file) return 1; // връщане при грешка при отваряне на char str; / / буфер за статичен ред // Четене и показване на редове в цикъл до eof докато (!file.getline(str, sizeof(str)).eof()) cout<< str << endl; // вывод прочитанной строки на экран cin.sync(); cin.get(); return 0; }

Този код под Windows OS също зависи от наличието на знак за нов ред в последния ред на файла, би било по-надеждно да направите това:

Докато (1) ( if (file.eof()) break; file.getline(str, sizeof(str)); cout<< str << endl; }

Изричните извиквания на методите за отваряне и затваряне не са задължителни. Наистина, извикването на конструктора с аргумент ви позволява да отворите файла веднага, в момента, в който се създава обектът на файловия поток:

файл ifstream("test.txt");

Вместо метода за затваряне можете да използвате оператора за изтриване, който автоматично ще извика деструктора на файловия обект и ще затвори файла. Кодът на цикъла while осигурява правилна проверка в края на файла.

ofstream клас: писане на файлове

Класът ofstream е проектиран да извежда данни от файлов поток. Основните методи на този клас са изброени по-долу.

Операторът за включване, описан по-рано, е удобен за организиране на запис в текстов файл:

Ofstream файл ("temp.txt"); if (!файл) връщане; за (int i=1; i<=3; i++) file << "Строка " << i << endl; file.close();

Двоични файлове

По принцип двоичните данни се обслужват като текстови данни. Разликата е, че ако двоичните данни са записани в определена логическа структура, те трябва да бъдат прочетени от файла в променлива от същия структурен тип.

Първият параметър на методите за запис и четене (адресът на блока за запис/четене) трябва да бъде от типа на символен указател char *, така че е необходимо изрично да се преобразува типа на адреса на структурата void *. Вторият параметър указва, че двоичните блокове на файла имат постоянен размер на байта, независимо от действителната дължина на записа. Следващото приложение дава пример за това как да създавате и показвате данни в обикновен бележник. След това записите на файла се четат последователно и се показват на конзолата.

#включи #включи #включи използване на пространство от имена std; struct Бележки ( // структура от данни на бележника char Име; // пълно име char Телефон; // phone int Възраст; // възраст); int main() ( setlocale(LC_ALL, "Russian"); Бележки Note1= ("Grozny Ioann Vasilyevich", "не е инсталиран", 60); Бележки Note2= ("Godunov Boris Fedorovich", "095-111-2233 ", 30 ); Бележки Note3= ( "Peter Romanov ", "812-333-2211 ", 20 ); ofstream ofile("Notebook.dat", ios::binary); ofile.write((char*)&Note1, sizeof ( Бележки)); // 1-ви блок ofile.write((char*)&Note2, sizeof(Notes)); // 2-ри блок ofile.write((char*)&Note3, sizeof(Notes)); // 3-ти блок ofile. close(); // затваряне на записания файл ifstream ifile("Notebook.dat", ios::binary); Бележки Забележка; // структурирана променлива char str; // буфер за статичен низ // Четене и показване на редове в цикъл, докато eof while (!ifile.read((char*)&Note, sizeof(Notes)).eof()) ( sprintf(str, "%s\tBody: %s\tAge: %d" , Note.Name, Note. Телефон, Забележка.Възраст);<< str << endl; } ifile.close(); // закрыть прочитанный файл cin.sync(); cin.get(); return 0; }

В резултат на изпълнението на този код се формира двоичен файл Notebook.dat от три блока от по 80 байта всеки (ако приемем, че символите са еднобайтови). Естествено, можете да използвате други методи за поточно предаване и да извършвате всякакви операции върху полетата на определена структура от данни.

клас fstream: произволен достъп до файлове

Да предположим, че нашият бележник е натрупал 100 записа и искаме да преброим 50-ия. Разбира се, можете да организирате цикъл и да прочетете всички записи от първия до дадения. Очевидно по-целенасочено решение е да зададете указателя на позицията на pos файла директно към запис 50 и да го прочетете:

ifstream ifile("Notebook.dat", ios::binary); int pos = 49 * sizeof(Бележки); търсене на ifile(pos); // търсене на 50-ия запис Забележки Забележка; //Бележки - структурата на "запис", описана по-горе ifile.read((char*)&Note, sizeof(Notes));

Такива операции за търсене са ефективни, ако файлът се състои от записи с известен и постоянен размер. За да замените съдържанието на произволен запис, трябва да отворите изходния поток в режим на промяна:

Ofstream ofile("Notebook.dat", ios::binary | ios::ate); int pos = 49 * sizeof(Бележки); ofile seekp(pos); // търсене на 50-та нота Notes Note50 = ("Елцин Борис Николаевич", "095-222-3322", 64); ofile.write((char*)&Note, sizeof(Notes)); // замяна

Ако не укажете флага ios::ate (или ios::app), тогава, когато отворите двоичния файл Notebook.dat, предишното му съдържание ще бъде изтрито!

И накрая, възможно е да отворите файл едновременно за четене/запис, като използвате методи, наследени от класа за поточно предаване fstream от неговите предшественици. Тъй като класът fstream е извлечен от istream и ostream (родители съответно на ifstream и ofstream), всички споменати по-горе методи стават достъпни за приложението.

Следващият пример разменя първия и третия запис във файла Notebook.dat.

#включи #включи #включи използване на пространство от имена std; struct Бележки ( char Име; char Телефон; int Възраст; ); int main() ( setlocale(LC_ALL, "Russian"); Notes Note1, Note3; // Отваряне на файл за едновременно четене/запис fstream file("Notebook.dat", ios::binary | ios::in | ios:: out); file.seekg(2 * sizeof(Notes)); // намиране и четене на Note3 file.read((char*)&Note3, sizeof(Notes)); file.seekg(0); // намиране и четене на Note1 file.read((char*)&Note1, sizeof(Notes)); file.seekg(0); // Note1<== Note3 file.write((char*)&Note3, sizeof(Notes)); file.seekg(2 * sizeof(Notes)); // Note3 <== Note1 file.write((char*)&Note1, sizeof(Notes)); char str; // Считывать и отображать записи в цикле, пока не eof file.seekg(0); // вернуться к началу файла while (!file.read((char*)&Note1, sizeof(Notes)).eof()) { sprintf(str, "%s\tТел: %s\tВозраст: %d", Note1.Name, Note1.Phone, Note1.Age); cout << str << endl; } file.close(); cin.sync(); cin.get(); return 0; }

Флаговете ios::in и ios::out трябва да бъдат посочени в конструктора на файловия обект, за да се позволят едновременни операции за четене и запис. В резултат на изпълнението на този код първият и третият запис на двоичния файл Notebook.dat ще бъдат разменени.

Има допълнителни примери по темата.

Повечето компютърни програми работят с файлове и следователно има нужда от създаване, изтриване, писане, четене, отваряне на файлове. Какво е файл? Файлът е наименована колекция от байтове, която може да се съхранява на някакво устройство за съхранение. Е, сега е ясно, че файлът е някаква последователност от байтове, която има собствено уникално име, като например .txt файл. Файлове с едно и също име не могат да бъдат в една и съща директория. Името на файла се разбира не само като неговото име, но и като разширение, например: file.txt и file.dat различни файлове, въпреки че имат едно и също име. Има такова нещо като пълното име на файловете - това е пълният адрес към файловата директория с името на файла, например: D:\docs\file.txt . Важно е да разберете тези основни понятия, в противен случай ще бъде трудно да работите с файлове.

За да работите с файлове, трябва да включите заглавен файл . AT няколко дефинирани класа и включени заглавни файлове въвеждане на файл и изход на файл.

Файловият I/O е подобен на стандартния I/O, единствената разлика е, че I/O не се извършва на екрана, а на файл. Ако входът/изходът към стандартните устройства се извършва с помощта на обектите cin и cout, тогава за организиране на файлов I/O е достатъчно да създадете свои собствени обекти, които могат да се използват подобно на операторите cin и cout.

Например, трябва да създадете текстов файл и да напишете реда Работа с файлове в C++ в него. За да направите това, трябва да направите следните стъпки:

  1. създайте обект от клас ofstream ;
  2. асоцииране на обект от клас с файла, в който ще се записва;
  3. напишете ред във файл;
  4. затворете файла.

Защо е необходимо да се създаде обект от класа ofstream, а не класа ifstream? Тъй като трябва да пишете във файл и ако трябва да прочетете данни от файл, тогава ще бъде създаден обект от класа ifstream.

// създаване на обект за запис във файла ofstream /*име на обект*/; // обект от клас ofstream

Нека наречем обекта - fout , Ето какво се случва:

Ofstream fout;

Защо се нуждаем от обект? От обекта се изисква да може да пише във файла. Обектът вече е създаден, но не е свързан с файла, в който трябва да бъде записан низът.

fout.open("cppstudio.txt"); // асоцииране на обект с файл

Чрез операцията точка получаваме достъп до метода на класа open(), в скобите на който посочваме името на файла. Посоченият файл ще бъде създаден в текущата директория с програмата. Ако съществува файл със същото име, съществуващият файл ще бъде заменен с нов. И така, файлът е отворен, остава да напишете желания ред в него. Прави се така:

Fout<< "Работа с файлами в С++"; // запись строки в файл

Използвайки операцията cast to stream във връзка с обекта fout, низът File Handling в C++ се записва във файл. Тъй като вече не е необходимо да се променя съдържанието на файла, той трябва да бъде затворен, тоест обектът трябва да бъде отделен от файла.

fout.close(); // затворете файла

В резултат на това беше създаден файл с реда Работа с файлове в C++.

Стъпки 1 и 2 могат да се комбинират, тоест в един ред да се създаде обект и да се асоциира с файл. Прави се така:

Ofstream fout("cppstudio.txt"); // създайте обект от класа ofstream и го асоциирайте с файла cppstudio.txt

Нека комбинираме целия код и да получим следната програма.

// file.cpp: дефинира входната точка за конзолното приложение. #include "stdafx.h" #include използване на пространство от имена std; int main(int argc, char* argv) ( ofstream fout("cppstudio.txt"); // създайте обект от класа ofstream за писане и го асоциирайте с файла cppstudio.txt fout<< "Работа с файлами в С++"; // запись строки в файл fout.close(); // закрываем файл system("pause"); return 0; }

Остава да проверим правилната работа на програмата и за това отваряме файла cppstudio.txt и погледнете съдържанието му, трябва да е -Работа с файлове на C++.

  1. създайте обект от класа ifstream и го асоциирайте с файла, от който ще се чете;
  2. четене на файл;
  3. затворете файла.
// file_read.cpp: дефинира входната точка за конзолното приложение. #include "stdafx.h" #include #включи използване на пространство от имена std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // правилно показване на кирилицата char buff; // междинен буфер за съхранение на текста, прочетен от файла ifstream fin("cppstudio.txt) "); // отворен файл за четене fin >><< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку system("pause"); return 0; }

Програмата показва два начина за четене от файл, първият използва операцията за прехвърляне към поток, вторият използва функцията getline() . В първия случай се чете само първата дума, а във втория се чете низ от 50 знака. Но тъй като във файла остават по-малко от 50 знака, знаците се четат до последния включително. Имайте предвид, че четенето втори път (ред 17) продължава след първата дума, а не от началото, тъй като първата дума е прочетенаред 14. Резултатът от програмата е показан на фигура 1.

Работа с файлове в C++ Натиснете произволен клавиш, за да продължите. . .

Фигура 1 - Работа с файлове в C++

Програмата работи правилно, но това не винаги е така, дори ако всичко с кода е наред. Например името на несъществуващ файл е предадено на програмата или е направена грешка в името. Какво тогава? В този случай изобщо нищо няма да се случи. Файлът няма да бъде намерен, което означава, че не е възможно да бъде прочетен. Следователно компилаторът ще игнорира редовете, където файлът се манипулира. В резултат на това програмата ще излезе правилно, но нищо няма да се покаже на екрана. Изглежда, че това е напълно нормална реакция към подобна ситуация. Но обикновен потребител няма да разбере какво е и защо ред от файла не се появи на екрана. И така, за да бъде всичко много ясно, C++ предоставя такава функция - is_open() , която връща цели числа: 1 - ако файлът е бил отворен успешно, 0 - ако файлът не е бил отворен. Нека финализираме програмата с отварянето на файла по такъв начин, че ако файлът не бъде отворен, да се покаже съответното съобщение.

// file_read.cpp: дефинира входната точка за конзолното приложение. #include "stdafx.h" #include #включи използване на пространство от имена std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // правилно показване на кирилица char buff; // междинен буфер за съхранение на текст, прочетен от файл ifstream fin("cppstudio.doc"); / / ( ВЪВЕДЕНО НЕПРАВИЛНО ИМЕ НА ФАЙЛ) if (!fin.is_open()) // ако файлът не е отворен cout<< "Файл не может быть открыт!\n"; // сообщить об этом else { fin >> баф; // прочете първата дума от файла cout<< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку } system("pause"); return 0; }

Резултатът от програмата е показан на фигура 2.

Файлът не може да бъде отворен! Натиснете произволен клавиш, за да продължите. . .

Фигура 2 - Работа с файлове в C++

Както можете да видите от Фигура 2, програмата съобщи, че файлът не може да бъде отворен. Следователно, ако програмата работи с файлове, се препоръчва да използвате тази функция, is_open(), дори ако сте сигурни, че файлът съществува.

Режими за отваряне на файлове

Режимите на отваряне на файлове определят как се използват файловете. За да зададете режима, класът ios_base предоставя константи, които определят режима на отваряне на файла (вижте таблица 1).

Режимите за отваряне на файлове могат да се задават директно при създаване на обект или при извикване на функцията open(). .

Ofstream fout("cppstudio.txt", ios_base::app); // отворете файла, за да добавите информация към края на файла fout. open("cppstudio.txt", ios_base::app); // отворете файла, за да добавите информация към края на файла

Режимите на отваряне на файлове могат да се комбинират с помощта на побитова булева операция или| , например: ios_base::out | ios_base::trunc - отворете файл за запис, след като го изчистите.

Обектите от класа ofstream, когато са свързани с файлове, по подразбиране съдържат режими на отваряне на файлове ios_base::out | ios_base::trunc . Тоест файлът ще бъде създаден, ако не съществува. Ако файлът съществува, съдържанието му ще бъде изтрито, а самият файл ще бъде готов за запис. Обектите от класа ifstream, когато са свързани с файл, имат по подразбиране режим на отваряне на файл ios_base::in - файлът е отворен само за четене. Режимът на отваряне на файл се нарича също флаг, за четливост ще използваме този термин в бъдеще. Таблица 1 не изброява всички флагове, но те трябва да са достатъчни, за да започнете.

Моля, обърнете внимание, че флаговете на ate и app са много сходни по описание, и двата преместват показалеца до края на файла, но флагът на приложението позволява запис само до края на файла, а флагът ate просто пренарежда флага към края на файла и не ограничава пространството за запис.

Нека разработим програма, която с помощта на операцията sizeof() ще изчисли характеристиките на основните типове данни в C++ и ще ги запише във файл. Характеристики:

  1. броя байтове, разпределени за типа данни
  2. максималната стойност, която даден тип данни може да съхранява.

Записването във файл трябва да бъде в следния формат:

/* Тип на данните байт максимална стойност bool = 1 255.00 char = 1 255.00 кратък int = 2 32767.00 Неподписан кратък int = 2 65535.00 int = 4 2147483647.00 Неспаден int = 4 4294967295.00 long int = 4 2147483647.00 неспоморен дълъг int = 4 42949672555.00 Лъгъне дълго плаващо = 8 9223372036854775800.00 двойно = 8 9223372036854775800.00 */

Такава програма вече е разработена по-рано в раздела, но там цялата информация за типовете данни беше изведена към стандартното изходно устройство и трябва да преработим програмата, така че информацията да се записва във файл. За да направите това, трябва да отворите файла в режим на запис, с предварително съкращаване на текущата информация за файла ( ред 14). След като файлът е създаден и успешно отворен (редове 16 - 20), вместо командата cout, в ред 22използвайте обекта fout. по този начин, вместо в екран, информацията за типовете данни ще бъде записана във файл.

// write_file.cpp: дефинира входната точка за конзолното приложение. #include "stdafx.h" #include #включи // работа с файлове #include // I/O манипулатори, използващи пространство от имена std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // свързване на обекта с файла, докато отваряте файла в режим на запис, изтривайки всички данни от него първо ofstream fout("data_types.txt" ", ios_base::out | ios_base::trunc); if (!fout.is_open()) // ако файлът не е бил отворен ( cout<< "Файл не может быть открыт или создан\n"; // напечатать соответствующее сообщение return 1; // выполнить выход из программы } fout << " data type " << "byte" << " " << " max value "<< endl // заглавия на колони <<"bool = " << sizeof(bool) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных bool*/ << (pow(2,sizeof(bool) * 8.0) - 1) << endl << "char = " << sizeof(char) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных char*/ << (pow(2,sizeof(char) * 8.0) - 1) << endl << "short int = " << sizeof(short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных short int*/ << (pow(2,sizeof(short int) * 8.0 - 1) - 1) << endl << "unsigned short int = " << sizeof(unsigned short int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned short int*/ << (pow(2,sizeof(unsigned short int) * 8.0) - 1) << endl << "int = " << sizeof(int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных int*/ << (pow(2,sizeof(int) * 8.0 - 1) - 1) << endl << "unsigned int = " << sizeof(unsigned int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных unsigned int*/ << (pow(2,sizeof(unsigned int) * 8.0) - 1) << endl << "long int = " << sizeof(long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long int*/ << (pow(2,sizeof(long int) * 8.0 - 1) - 1) << endl << "unsigned long int = " << sizeof(unsigned long int) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных undigned long int*/ << (pow(2,sizeof(unsigned long int) * 8.0) - 1) << endl << "float = " << sizeof(float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных float*/ << (pow(2,sizeof(float) * 8.0 - 1) - 1) << endl << "long float = " << sizeof(long float) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных long float*/ << (pow(2,sizeof(long float) * 8.0 - 1) - 1) << endl << "double = " << sizeof(double) << " " << fixed << setprecision(2) /*вычисляем максимальное значение для типа данных double*/ << (pow(2,sizeof(double) * 8.0 - 1) - 1) << endl; fout.close(); // программа больше не использует файл, поэтому его нужно закрыть cout << "Данные успешно записаны в файл data_types.txt\n"; system("pause"); return 0; }

Невъзможно е да не забележите, че промените в програмата са минимални и всичко това благодарение на факта, че стандартният вход / изход и файловият вход / изход се използват по абсолютно същия начин. В края на програмата,ред 45ние изрично затворихме файла, въпреки че това не е задължително, счита се за добра програмна практика. Струва си да се отбележи, че всички функции и манипулатори, използвани за форматиране на стандартен вход/изход, са приложими и за въвеждане/изход на файлове. Следователно не са възникнали грешки, когато операторът cout е заменен от обектфут.

Последна актуализация: 31.10.2015г

Два класа са проектирани да работят с директории в пространството на имената System.IO: Directory и DirectoryInfo.

Клас справочник

Класът Directory предоставя редица статични методи за управление на директории. Някои от тези методи са:

    CreateDirectory(path) : създава директория на посочения път

    Delete(path) : изтрива директорията по дадения път

    Exists(path) : Определя дали директорията на посочения път съществува. Връща true, ако съществува, false, ако не.

    GetDirectories(path) : Получава списък с директории в пътя

    GetFiles(path) : Получава списък с файлове в пътя на директорията

    Преместване (име на изходна директория, име на целева директория): премества директорията

    GetParent(path) : вземете родителската директория

Клас DirectoryInfo

Този клас предоставя функционалност за създаване, изтриване, преместване и други операции с директории. В много отношения той е подобен на Directory. Някои от неговите свойства и методи са:

    Create() : създава директория

    CreateSubdirectory(path) : създава поддиректория на посочения път

    Delete() : изтрива директория

    Свойство Exists: определя дали директорията съществува

    GetDirectories() : Получава списък с директории

    GetFiles() : вземете списък с файлове

    MoveTo(destDirName) : премества директория

    Родителско свойство: получаване на родителската директория

    Основно свойство: получаване на основната директория

Нека да разгледаме примери за използването на тези класове

Получаване на списък с файлове и поддиректории

низ dirName = "C:\\"; if (Directory.Exists(dirName)) ( Console.WriteLine("Поддиректории:"); низ dirs = Directory.GetDirectories(dirName); foreach (низ s в директории) ( Console.WriteLine(s); ) Console.WriteLine( ); Console.WriteLine("Файлове:"); низови файлове = Directory.GetFiles(dirName); foreach (низове във файлове) ( Console.WriteLine(s); ) )

Обърнете внимание на използването на наклонени черти в имената на файловете. Или използваме двойна наклонена черта: "C:\\" , или единична, но след това поставяме знака @ пред целия път: @"C:\Program Files"

Създайте директория

път на низ = @"C:\SomeDir"; низ подпът = @"program\avalon"; DirectoryInfo dirInfo = нов DirectoryInfo(път); if (!dirInfo.Exists) ( dirInfo.Create(); ) dirInfo.CreateSubdirectory(подпът);

Първо проверяваме дали има такава директория, защото ако съществува, тогава няма да е възможно да я създадем и приложението ще изведе грешка. В резултат на това ще получим следния път: "C:\SomeDir\program\avalon"

Получаване на информация за директория

string dirName = "C:\\Програмни файлове"; DirectoryInfo dirInfo = нова DirectoryInfo(dirName); Console.WriteLine($"Име на директория: (dirInfo.Name)"); Console.WriteLine($"Пълно име на директория: (dirInfo.FullName)"); Console.WriteLine($"Време за създаване на директория: (dirInfo.CreationTime)"); Console.WriteLine($"Главна директория: (dirInfo.Root)");

Изтриване на директория

Ако просто приложим метода Delete към непразна папка, която съдържа всякакви файлове или поддиректории, тогава приложението ще изведе грешка. Следователно трябва да предадем допълнителен булев тип параметър към метода Delete, който ще покаже, че папката трябва да бъде изтрита с цялото й съдържание:

String dirName = @"C:\SomeFolder"; опитайте ( 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);

Преместване на директория

низ oldPath = @"C:\SomeFolder"; низ newPath = @"C:\SomeDir"; DirectoryInfo dirInfo = нова 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 KB), FAT16 за малки дискове, FAT32 за FLASH устройства с голям капацитет (до 32 GB).

Помислете за структурата на файловата система, използвайки 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 байта) - броят на секторите на диска;
  • 0x2C (4 байта) – номер на клъстера на основната директория;
  • 0x47 (11 байта) – етикет на тома;
  • 0x1FE (2 байта) - Сигнатура на зареждащия сектор (55 AA).

Секторът с информация за файловата система съдържа:

  • 0x00 (4 байта) – подпис (52 52 61 41);
  • 0x1E4 (4 байта) – подпис (72 72 41 61);
  • 0x1E8 (4 байта) – брой свободни клъстери, -1 ако не е известен;
  • 0x1EC (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 байта – номер на първия клъстер;
  • останалите знаци от дългото име.

Работа с файлове в C

За програмиста отвореният файл се представя като последователност от данни, които се четат или записват. Когато даден файл се отвори, той се свързва с I/O поток. Изходната информация се записва в потока, входната информация се чете от потока.

Когато потокът е отворен за I/O, той се свързва със стандартната структура от тип FILE, която е дефинирана в stdio.h. Структурата FILE съдържа необходимата информация за файла.

Отварянето на файл се извършва с помощта на функцията fopen(), която връща указател към структура от тип FILE, която може да се използва за последващи операции с файла.

ФАЙЛ *fopen(име, тип);


име е името на файла, който ще се отвори (включително пътя),
тип е указател към низ от знаци, които определят как се осъществява достъп до файла:
  • "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

#включи
int main() (
ФАЙЛ *fp;
char name = "my.txt" ;
if ((fp = fopen(име, "r" )) == NULL )
{
printf( „Файлът не можа да се отвори“);
getchar();
връщане 0;
}
// отварянето на файла е успешно
... // необходими действия върху данните
fclose(fp);
getchar();
връщане 0;
}

Четене на знак от файл:

char fgetc(поток);


Аргументът на функцията е указател към поток от тип FILE. Функцията връща кода на прочетения знак. Ако се достигне краят на файла или възникне грешка, EOF константата се връща.

Записване на знак във файл:

fputc(символ, поток);

Аргументите на функцията са символ и указател към поток от тип FILE. Функцията връща кода на прочетения знак.

Функциите fscanf() и fprintf() са подобни на функциите scanf() и printf(), но работят с файлове с данни и имат файлов указател като първи аргумент.

fscanf(stream, "InputFormat" , args);