Operace souborového I/O v C++ je téměř stejná jako u běžných I/O (ale s několika nuancemi).

Soubor I/O tříd

Tady je tři hlavní třídy I/O souborů v C++:

outstream(je dítětem třídy);

fstream(je potomkem třídy iostream).

S těmito třídami můžete provádět jednosměrný vstup souborů, jednosměrný výstup souborů a obousměrný vstup/výstup souborů. Chcete-li je používat, stačí připojit fstream.

Na rozdíl od proudů cout, cin, cerr a clog, které jsou okamžitě použitelné, musí být datové proudy souborů explicitně nastaveny programátorem. To znamená, že chcete-li otevřít soubor pro čtení a/nebo zápis, musíte vytvořit objekt příslušné třídy I/O souboru a zadat název souboru jako parametr. Poté pomocí operátorů vložení (<<) или извлечения (>>), můžete zapisovat data do souboru nebo číst obsah souboru. Poté konečný - musíte soubor zavřít: explicitně zavolat metoda close(). nebo prostě nechte proměnnou I/O souboru mimo rozsah (třída I/O souboru za nás tento soubor automaticky zavře).

Výstup souboru

Třída ofstream se používá k zápisu do souboru. Například:

#zahrnout #zahrnout #zahrnout // použít exit() int main() ( pomocí jmenného prostoru std; // ofstream se používá k zápisu dat do souboru // Vytvořit soubor SomeText.txt ofstream outf("SomeText.txt"); // Pokud můžeme 't otevřít tento soubor pro zápis dat do if (!outf) ( // Poté vytiskněte chybovou zprávu a spusťte 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 автоматически закроет наш файл }

#zahrnout

#zahrnout

#zahrnout // pro použití exit()

int main()

pomocí jmenného prostoru std ;

// ofstream se používá k zápisu dat do souboru

// Vytvořte soubor SomeText.txt

ofstream outf("NějakýText.txt" ) ;

// Pokud tento soubor nemůžeme otevřít, abychom do něj zapsali data

if(!outf)

// Poté vytiskněte chybovou zprávu a spusťte exit()

cerr<< << endl ;

exit(1) ;

// Zapište do souboru následující dva řádky

outf<< "See line #1!" << endl ;

outf<< "See line #2!" << endl ;

návrat 0;

// Když outf přejde mimo rozsah, destruktor třídy ofstream automaticky zavře náš soubor

Pokud se podíváte do adresáře projektu ( RMB na kartě s názvem vašeho souboru .cpp v sadě Visual Studio > "Otevřít obsahující složku"), uvidíte soubor s názvem SomeText.txt, který obsahuje následující řádky:

Viz řádek #1!
Viz řádek #2!

Upozorňujeme, že můžeme také použít metodu put(). zapsat jeden znak do souboru.

Vstup souboru

#zahrnout #zahrnout #zahrnout #zahrnout // k použití exit() int main() ( pomocí jmenného prostoru std; // ifstream se používá ke čtení obsahu souboru // Zkuste si přečíst obsah souboru SomeText.txt ifstream inf("SomeText.txt") ; // Pokud nemůžeme tento soubor otevřít a přečíst si jeho obsah 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; }

#zahrnout

#zahrnout

#zahrnout

#zahrnout // pro použití exit()

int main()

pomocí jmenného prostoru std ;

// ifstream se používá ke čtení obsahu souboru

// Nemůžeme-li tento soubor otevřít a přečíst si jeho obsah

if(!inf)

// Poté vytiskněte následující chybovou zprávu a spusťte exit()

cerr<< << endl ;

exit(1) ;

// Dokud existují data, která můžeme číst

zatímco (inf)

// Poté tato data přesuneme do řetězce, který následně zobrazíme na obrazovce

string strInput ;

inf >> strInput ;

cout<< strInput << endl ;

návrat 0;

// Když inf přejde mimo rozsah, destruktor třídy ifstream automaticky zavře náš soubor

Vidět
čára
#1!
Vidět
čára
#2!

Hmm, to není úplně to, co jsme chtěli. Jak již víme z předchozích lekcí, operátor extrakce pracuje s „formátovanými daty“, tzn. ignoruje všechny mezery, tabulátory a nové řádky. K přečtení veškerého obsahu tak, jak je, bez jeho rozdělení na části (jako v příkladu výše), musíme použít metoda getline().:

#zahrnout #zahrnout #zahrnout #zahrnout // použít exit() int main() ( pomocí jmenného prostoru std; // ifstream se používá ke čtení obsahu souborů // Pokusíme se přečíst obsah souboru SomeText.txt ifstream inf("SomeText.txt") ; // Pokud nemůžeme otevřít soubor a přečíst si jeho obsah if (!inf) ( // Pak vytiskněte následující chybovou zprávu a ukončete() 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 автоматически закроет наш файл }

#zahrnout

#zahrnout

#zahrnout

#zahrnout // pro použití exit()

int main()

pomocí jmenného prostoru std ;

// ifstream se používá ke čtení obsahu souborů

ifstream inf("NějakýText.txt" ) ;

// Pokud nemůžeme otevřít soubor a přečíst si jeho obsah

if(!inf)

// Poté vytiskněte následující chybovou zprávu a spusťte exit()

cerr<< "Ach, SomeText.txt nelze otevřít pro čtení!"<< endl ;

exit(1) ;

zatímco (inf)

string strInput ;

getline (inf , strInput ) ;

cout<< strInput << endl ;

návrat 0;

// Když inf přejde mimo rozsah, destruktor třídy ifstream automaticky zavře náš soubor

Výsledek spuštění výše uvedeného programu je:

bufferovaný výstup

Výstup v C++ lze uložit do vyrovnávací paměti. To znamená, že vše, co je na výstupu do souborového proudu, nelze okamžitě zapsat na disk (do konkrétního souboru). Děje se tak především z výkonnostních důvodů. Když jsou data vyrovnávací paměti zapsána na disk, nazývá se to vymazání vyrovnávací paměti. Jedním ze způsobů, jak vyčistit vyrovnávací paměť, je zavřít soubor. V tomto případě se celý obsah vyrovnávací paměti přesune na disk a poté se soubor zavře.

Ukládání do vyrovnávací paměti obvykle není problém, ale za určitých okolností může způsobit problémy neopatrným nováčkům. Například když jsou data uložena ve vyrovnávací paměti a program předčasně ukončí jejich provádění (buď v důsledku pádu nebo voláním ). V takových případech nejsou destruktory tříd I/O souborů provedeny, soubory nejsou nikdy uzavřeny, buffery nejsou vyprázdněny a naše data jsou navždy ztracena. Proto je dobré před voláním exit() explicitně zavřít všechny otevřené soubory.

Vyrovnávací paměť můžete také vymazat ručně pomocí metoda ostream::flush(). nebo zasláním std::flush do výstupního proudu. Každá z těchto metod může být užitečná pro zajištění okamžitého zápisu obsahu vyrovnávací paměti na disk v případě pádu programu.

Zajímavá nuance: Protože std::endl; také vymaže výstupní proud, pak jeho nadměrné používání (vedoucí ke zbytečnému vyprázdnění vyrovnávací paměti) může ovlivnit výkon programu (protože vyprázdnění vyrovnávací paměti může být v některých případech drahé). Z tohoto důvodu programátoři dbající na výkon často používají \n místo std::endl k vložení nového řádku do výstupního proudu, aby se vyhnuli zbytečnému vyplachování vyrovnávací paměti.

Režimy otevírání souborů

Co se stane, když se pokusíme zapsat data do již existujícího souboru? Opětovné spuštění výše uvedeného programu (úplně první) ukazuje, že původní soubor je při dalším spuštění programu zcela přepsán. Co když ale potřebujeme přidat data na konec souboru? Ukázalo se, že datový proud souboru má volitelný druhý parametr, který vám umožňuje sdělit programátorovi, jak soubor otevřít. Jako tento parametr můžete předat následující vlajky(které jsou ve třídě ios):

aplikace- otevře soubor v režimu připojení;

jedl- přejde na konec souboru před čtením/zápisem;

binární- otevře soubor v binárním režimu (místo textového režimu);

v- otevře soubor v režimu čtení (výchozí pro ifstream);

ven- otevře soubor v režimu zápisu (výchozí pro ofstream);

trunc- smaže soubor, pokud již existuje.

Můžete zadat více příznaků najednou pomocí .

ifstream standardně funguje v režimu ios::in;

ofstream standardně funguje v režimu ios::out;

fstream standardně běží v režimu ios::in NEBO ios::out, což znamená, že můžete buď číst obsah souboru, nebo zapisovat data do souboru.

Nyní napíšeme program, který přidá dva řádky do dříve vytvořeného souboru SomeText.txt:

#zahrnout #zahrnout // pro použití exit() #include int main() ( pomocí jmenného prostoru std; // Předáním příznaku ios:app sdělíme fstreamu, že přidáme naše data k již existujícím datům souboru, // soubor nepřepíšeme. Ne Není třeba předat příznak ios::out , // protože ofstream má výchozí hodnotu ios::out ofstream outf("SomeText.txt", ios::app); // Pokud nemůžeme otevřít soubor pro zápis dat, pokud (!outf) ( // Poté vypíše následující chybovou zprávu a 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 автоматически закроет наш файл }

#zahrnout

#zahrnout // pro použití exit()

#zahrnout

int main()

pomocí jmenného prostoru std ;

// Předáním příznaku ios:app sdělíme fstreamu, že přidáme naše data k již existujícím datům souboru,

// soubor nepřepíšeme. Nemusíme předávat příznak ios::out,

// protože ofstream má výchozí režim ios::out

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

// Pokud nemůžeme otevřít soubor pro zápis dat

if(!outf)

// Poté vytiskněte následující chybovou zprávu a spusťte exit()

cerr<< "Ach, SomeText.txt nelze otevřít pro zápis!"<< endl ;

exit(1) ;

Mechanismus I/O vyvinutý společností , neodpovídá dnes obecně přijímanému stylu objektově orientovaného programování, navíc aktivně využívá ukazatelové operace, které jsou v moderních prostředích spouštění zabezpečeného kódu považovány za potenciálně nebezpečné. Alternativou pro vývoj aplikací je standardní mechanismus I/O tříd poskytovaný standardem jazyka C++.

Otevírání souborů

Nejčastěji používané třídy jsou ifstream pro čtení, ofstream pro zápis a fstream pro úpravu souborů.

Všechny třídy I/O s vlákny jsou nepřímo odvozeny od společného předka ios a plně zdědí jeho funkčnost. Například výčtový datový člen open_mode určuje režim otevření souboru, který je definován následovně:

Enum open_mode ( app, binary, in, out, trunc, ate );

Níže jsou uvedeny možné hodnoty příznaků a jejich účel.

Chcete-li například otevřít soubor s názvem test.txt pro čtení binárních dat, napište:

ifstream soubor; file.open("test.txt", ios::in | ios::binary);

Logický operátor OR (|) vám umožňuje sestavit režim s libovolnou kombinací příznaků. Abyste při otevírání souboru zápisem omylem nepřepsali existující soubor se stejným názvem, musíte použít následující formulář:

offstream soubor; file.open("test.txt", ios::out | ios::app);

Předpokládá se, že k projektu je připojen příslušný hlavičkový soubor:

#zahrnout

Chcete-li zkontrolovat, zda byl soubor úspěšně otevřen, můžete použít konstrukci

If (!file) ( // Zpracování chyby při otevření souboru )

Operátoři inkluze a těžby

Přepsáno ve třídách zpracování souborů zahrnout operátora (<<) записывает данные в файловый поток. Как только вы открыли файл для записи, можно записывать в него текстовую строку целиком:

soubor<< "Это строка текста";

Textový řetězec můžete také napsat po částech:

soubor<< "Это " << "строка " << "текста";

Příkaz endl ukončí vstup řádku návratem vozíku:

soubor<< "Это строка текста" << endl;

Pomocí operátoru include je snadné zapsat hodnoty proměnných nebo prvků pole do souboru:

Ofstream soubor("Temp.txt"); char buff = "Textové pole obsahuje proměnné"; int vx = 100; plovák pi = 3,14159; soubor<< buff << endl << vx << endl << pi << endl;

V důsledku spuštění kódu se vygenerují tři řádky textového souboru Temp.txt:

Textové pole obsahuje proměnné 100 3,14159

Všimněte si, že číselné hodnoty se do souboru zapisují jako textové řetězce, nikoli binární hodnoty.

operátor extraktu(>>) dělá opak. Zdá se, že k extrahování znaků ze souboru Temp.txt napsaného dříve, musíte napsat kód jako následující:

ifstream soubor("Temp.txt"); char buff; intvx; floatpi; soubor >> buff >> vx >> pi;

Operátor extrakce se však zastaví u prvního oddělovače (mezera, tabulátor nebo nový řádek), na který narazí. Při analýze věty „Textové pole obsahuje proměnné“ se tedy do pole buff zapíše pouze slovo „Text“, mezera se ignoruje a slovo „pole“ se stane hodnotou celočíselné proměnné vx a kódu provedení se „zničí“ s nevyhnutelným porušením datové struktury. Dále si při probírání třídy ifstream ukážeme, jak správně organizovat čtení souboru z předchozího příkladu.

ifstream třída: čtení souborů

Jak název napovídá, třída ifstream je navržena pro vstup datového proudu souboru. Hlavní metody třídy jsou uvedeny níže. Většina z nich je zděděna z třídy istream a přetížena rodičovskou funkcí. Například funkce get je v závislosti na parametru volání schopna číst nejen jeden znak, ale i blok znaků.

Nyní je jasné, jak je třeba upravit předchozí příklad, aby použití operátoru extrakce dat poskytlo očekávaný výsledek:

ifstream soubor("Temp.txt"); char buff; intvx; floatpi; file.getline(buff, sizeof(buff)); soubor >> vx >> pi:

Metoda getline přečte první řádek souboru až do konce a operátor >> přiřadí hodnoty proměnným.

Následující příklad ukazuje přidání dat do textového souboru a následné čtení celého souboru. Smyčka while (1) se používá místo while(!file2.eof()) z důvodů uvedených v .

#zahrnout #zahrnout pomocí jmenného prostoru std; int main() ( soubor ofstream; file.open("test.txt",ios::out|ios::app); if (!soubor) ( 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; }

Následující příklad prochází čtením řádků ze souboru test.txt a jejich zobrazením na konzole.

#zahrnout #zahrnout pomocí jmenného prostoru std; int main() ( soubor ifstream; // vytvoření souboru objektu streamu file.open("test.txt"); // otevření souboru pro čtení if (!soubor) návrat 1; // návrat při chybě otevření char str; / / static line buffer // Čtení a zobrazování řádků ve smyčce až do eof while (!file.getline(str, sizeof(str)).eof()) cout<< str << endl; // вывод прочитанной строки на экран cin.sync(); cin.get(); return 0; }

Tento kód pod OS Windows také závisí na přítomnosti znaku nového řádku v posledním řádku souboru, bylo by spolehlivější to udělat:

While (1) ( if (file.eof()) break; file.getline(str, sizeof(str)); cout<< str << endl; }

Explicitní volání metod open a close jsou volitelné. Volání konstruktoru s argumentem vám skutečně umožní otevřít soubor okamžitě, v okamžiku vytvoření objektu streamu souboru:

ifstream soubor("test.txt");

Místo metody close můžete použít operátor delete, který automaticky zavolá destruktor objektu souboru a zavře soubor. Kód smyčky while poskytuje správnou kontrolu konce souboru.

ofstream class: zápis souborů

Třída ofstream je navržena pro výstup dat z datového proudu souborů. Hlavní metody této třídy jsou uvedeny níže.

Operátor include popsaný výše je vhodný pro organizaci zápisu do textového souboru:

Ofstream soubor("temp.txt"); if (!soubor) return; pro (int i=1; i<=3; i++) file << "Строка " << i << endl; file.close();

Binární soubory

V zásadě se binární data obsluhují jako textová data. Rozdíl je v tom, že pokud jsou binární data zapsána v určité logické struktuře, musí být načtena ze souboru do proměnné stejného typu struktury.

První parametr metod zápisu a čtení (adresa bloku zápisu/čtení) musí být typu znakového ukazatele char * , je tedy nutné explicitně převést typ adresy struktury void *. Druhý parametr určuje, že binární bloky souboru mají konstantní velikost bajtů bez ohledu na skutečnou délku záznamu. Následující příloha uvádí příklad, jak vytvořit a zobrazit data v jednoduchém poznámkovém bloku. Záznamy souboru jsou pak postupně čteny a zobrazeny na konzole.

#zahrnout #zahrnout #zahrnout pomocí jmenného prostoru std; struct Poznámky ( // datová struktura zápisníku char Name; // celé jméno char Phone; // phone int Age; // age ); int main() ( setlocale(LC_ALL, "Russian"); Notes Note1= ("Grozny Ioann Vasilyevich", "nenainstalováno", 60 ); Notes Note2= ("Godunov Boris Fedorovich", "095-111-2233 ", 30 ); Notes Note3= ( "Peter Romanov ", "812-333-2211", 20 ); ofstream ofile("Notebook.dat", ios::binary); ofile.write((char*)&Note1, sizeof ( Notes)); // 1. blok souboru.write((znak*)&Poznámka2, velikost(Poznámky)); // 2. blok souboru.write((znak*)&Poznámka3, velikost(Poznámky)); / / 3. blok souboru. close(); // zavře zapsaný soubor ifstream ifile("Notebook.dat", ios::binary); Notes Note; // strukturovaná proměnná char str; // vyrovnávací paměť statického řetězce // Čtení a zobrazování řádků ve smyčce, dokud eof while (!file.read((char*)&Note, sizeof(Notes)).eof()) ( sprintf(str, "%s\tBody: %s\tAge: %d" , Note.Name, Note. Telefon, Poznámka.Věk); cout<< str << endl; } ifile.close(); // закрыть прочитанный файл cin.sync(); cin.get(); return 0; }

V důsledku provedení tohoto kódu se vytvoří binární soubor Notebook.dat ze tří bloků po 80 bajtech (za předpokladu, že znaky jsou jednobajtové). Samozřejmě můžete použít jiné metody streamování a provádět libovolné operace na polích konkrétní datové struktury.

třída fstream: náhodný přístup k souboru

Předpokládejme, že náš zápisník nashromáždil 100 záznamů a my chceme počítat 50. Samozřejmě můžete uspořádat smyčku a přečíst všechny záznamy od prvního po daný. Je zřejmé, že cílenějším řešením je nastavit ukazatel polohy souboru pos přímo na položku 50 a přečíst ji:

ifstream ifile("Notebook.dat", ios::binary); int pos = 49 * sizeof(Notes); ifile hledat (pos); // hledání 50. položky Poznámky Poznámka; //Poznámky - struktura "záznam" popsaná výše ifile.read((char*)&Note, sizeof(Notes));

Takové vyhledávací operace jsou účinné, pokud soubor obsahuje záznamy o známé a konstantní velikosti. Chcete-li nahradit obsah libovolné položky, musíte otevřít výstupní proud v režimu úprav:

Ofstream ofile("Notebook.dat", ios::binary | ios::ate); int pos = 49 * sizeof(Notes); ofile seekp(pos); // hledání 50. noty Notes Note50 = ("Jelcin Boris Nikolajevič", "095-222-3322", 64); ofile.write((char*)&Poznámka, velikost(Poznámky)); // výměna, nahrazení

Pokud nezadáte příznak ios::ate (nebo ios::app), pak při otevření binárního souboru Notebook.dat bude jeho předchozí obsah vymazán!

Konečně je možné otevřít soubor současně pro čtení/zápis pomocí metod zděděných streamovací třídou fstream od svých předchůdců. Vzhledem k tomu, že třída fstream je odvozena od istream a ostream (rodiče ifstream a ofstream), jsou pro aplikaci dostupné všechny výše uvedené metody.

Následující příklad zamění první a třetí položku v souboru Notebook.dat.

#zahrnout #zahrnout #zahrnout pomocí jmenného prostoru std; struct Poznámky ( char Jméno; char Telefon; int Věk; ); int main() ( setlocale(LC_ALL, "Russian"); Notes Note1, Note3; // Otevření souboru pro současné čtení/zápis fstream file("Notebook.dat", ios::binary | ios::in | ios:: out); file.seekg(2 * sizeof(Notes)); // najít a přečíst Note3 file.read((char*)&Note3, sizeof(Notes)); file.seekg(0); // najít a přečíst Note1 file.read((char*)&Note1, sizeof(Notes)); file.seekg(0); // Poznámka1<== 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; }

Příznaky ios::in a ios::out musí být specifikovány v konstruktoru objektu souboru, aby bylo možné provádět souběžné operace čtení a zápisu. V důsledku provedení tohoto kódu dojde k záměně prvního a třetího záznamu binárního souboru Notebook.dat.

K tématu jsou další příklady.

Většina počítačových programů pracuje se soubory, a proto je potřeba soubory vytvářet, mazat, zapisovat, číst, otevírat. Co je soubor? Soubor je pojmenovaná kolekce bajtů, která může být uložena na nějakém úložném zařízení. No, teď je jasné, že soubor je nějaká posloupnost bajtů, která má svůj vlastní jedinečný název, například soubor .txt. Soubory se stejným názvem nemohou být ve stejném adresáři. Název souboru je chápán nejen jako jeho název, ale také jako přípona, například: soubor.txt a soubor.dat různé soubory, i když mají stejný název. Existuje něco jako úplný název souborů - jedná se o úplnou adresu do adresáře souboru s názvem souboru, například: D:\docs\file.txt . Je důležité porozumět těmto základním pojmům, jinak bude obtížné pracovat se soubory.

Chcete-li pracovat se soubory, musíte zahrnout soubor záhlaví . V několik definovaných tříd a hlavičkových souborů vstup do souboru a výstup souboru.

File I/O je podobný standardnímu I/O, jediný rozdíl je v tom, že I/O se neprovádí na obrazovku, ale do souboru. Pokud je vstup/výstup na standardní zařízení prováděn pomocí objektů cin a cout, pak pro organizaci souborových I/O stačí vytvořit vlastní objekty, které lze použít podobně jako operátory cin a cout.

Například je potřeba vytvořit textový soubor a do něj napsat řádek Práce se soubory v C++. Chcete-li to provést, musíte provést následující kroky:

  1. vytvořit objekt třídy proudu ;
  2. asociovat objekt třídy se souborem, do kterého se má zapisovat;
  3. zapsat řádek do souboru;
  4. zavřete soubor.

Proč je nutné vytvořit objekt třídy ofstream a ne třídy ifstream? Protože potřebujete zapisovat do souboru a pokud byste potřebovali číst data ze souboru, pak by se vytvořil objekt třídy ifstream.

// vytvořte objekt pro zápis do souboru ofstream /*jméno objektu*/; // objekt třídy proudu

Nazvěme objekt - fout , Zde je to, co se stane:

Ofstream fout;

Proč potřebujeme předmět? Objekt musí být schopen zapisovat do souboru. Objekt již byl vytvořen, ale není přidružen k souboru, do kterého má být řetězec zapsán.

fout.open("cppstudio.txt"); // asociovat objekt se souborem

Prostřednictvím operace tečka získáme přístup k metodě třídy open(), v jejíž závorce uvedeme název souboru. Zadaný soubor bude vytvořen v aktuálním adresáři s programem. Pokud existuje soubor se stejným názvem, stávající soubor bude nahrazen novým. Soubor je tedy otevřený, zbývá do něj napsat požadovaný řádek. Dělá se to takto:

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

Pomocí operace cast to stream ve spojení s objektem fout je řetězec File Handling v C++ zapsán do souboru. Protože již není nutné měnit obsah souboru, musí být uzavřen, to znamená, že objekt by měl být oddělen od souboru.

fout.close(); // zavřete soubor

V důsledku toho byl vytvořen soubor s řádkem Práce se soubory v C++.

Kroky 1 a 2 lze kombinovat, to znamená v jednom řádku vytvořit objekt a přiřadit jej k souboru. Dělá se to takto:

Ofstream fout("cppstudio.txt"); // vytvoří objekt třídy ofstream a přiřadí jej k souboru cppstudio.txt

Zkombinujme celý kód a získáme následující program.

// soubor.cpp: definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include pomocí jmenného prostoru std; int main(int argc, char* argv) ( ofstream fout("cppstudio.txt"); // vytvořte objekt třídy ofstream pro zápis a přidružte jej k souboru cppstudio.txt fout<< "Работа с файлами в С++"; // запись строки в файл fout.close(); // закрываем файл system("pause"); return 0; }

Zbývá zkontrolovat správnou činnost programu, a proto soubor otevřeme cppstudio.txt a podívejte se na jeho obsah, mělo by to být - Práce se soubory v C++.

  1. vytvořit objekt třídy ifstream a přiřadit jej k souboru, ze kterého se má číst;
  2. číst soubor;
  3. zavřete soubor.
// file_read.cpp: definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include #zahrnout pomocí jmenného prostoru std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // správné zobrazení char buff v azbuce; ​​// mezipaměť pro ukládání textu načteného ze souboru ifstream fin("cppstudio.txt "); // otevření souboru pro čtení fin >><< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку system("pause"); return 0; }

Program ukazuje dva způsoby čtení ze souboru, prvním je použití operace přenosu do streamu, druhým je použití funkce getline() . V prvním případě se přečte pouze první slovo a ve druhém případě se přečte řetězec 50 znaků. Ale protože v souboru zbývá méně než 50 znaků, znaky se přečtou až do posledního včetně. Všimněte si, že čtení podruhé (řádek 17) pokračoval po prvním slově, a ne od začátku, protože první slovo bylo přečtenořádek 14. Výsledek programu je na obrázku 1.

Práce se soubory v C++ Pokračujte stisknutím libovolné klávesy. . .

Obrázek 1 - Práce se soubory v C++

Program fungoval správně, ale není tomu tak vždy, i když je vše v pořádku s kódem. Programu byl například předán název neexistujícího souboru nebo došlo k chybě v názvu. Co pak? V tomto případě se nestane vůbec nic. Soubor nebude nalezen, což znamená, že jej nelze přečíst. Proto bude kompilátor ignorovat řádky, kde je soubor zpracováván. V důsledku toho se program správně ukončí, ale na obrazovce se nic nezobrazí. Zdálo by se, že jde o zcela normální reakci na takovou situaci. Prostý uživatel však nepochopí, o co jde a proč se na obrazovce neobjevil řádek ze souboru. Aby bylo vše velmi jasné, C++ poskytuje takovou funkci - is_open() , která vrací celočíselné hodnoty: 1 - pokud byl soubor úspěšně otevřen, 0 - pokud soubor nebyl otevřen. Dokončíme program otevřením souboru tak, že pokud se soubor neotevře, zobrazí se odpovídající zpráva.

// file_read.cpp: definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include #zahrnout pomocí jmenného prostoru std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // správné zobrazení cyrilického znakového buffu; // přechodná vyrovnávací paměť pro text načtený ze souboru ifstream fin("cppstudio.doc"); / / ( ZADAL NESPRÁVNÝ NÁZEV SOUBORU) if (!fin.is_open()) // pokud soubor není otevřený cout<< "Файл не может быть открыт!\n"; // сообщить об этом else { fin >> buff; // přečtení prvního slova ze souboru cout<< buff << endl; // напечатали это слово fin.getline(buff, 50); // считали строку из файла fin.close(); // закрываем файл cout << buff << endl; // напечатали эту строку } system("pause"); return 0; }

Výsledek programu je na obrázku 2.

Soubor nelze otevřít! Pokračujte stiskem libovolné klávesy. . .

Obrázek 2 - Práce se soubory v C++

Jak můžete vidět na obrázku 2, program hlásil, že soubor nelze otevřít. Pokud tedy program pracuje se soubory, doporučuje se použít tuto funkci is_open() , i když jste si jisti, že soubor existuje.

Režimy otevírání souborů

Režimy otevírání souborů určují způsob použití souborů. Pro nastavení režimu poskytuje třída ios_base konstanty, které určují režim otevírání souboru (viz Tabulka 1).

Režimy otevírání souborů lze nastavit přímo při vytváření objektu nebo při volání funkce open(). .

Ofstream fout("cppstudio.txt", ios_base::app); // otevření souboru pro přidání informací na konec souboru fout. open("cppstudio.txt", ios_base::app); // otevřete soubor pro přidání informací na konec souboru

Režimy otevření souboru lze kombinovat pomocí bitové booleovské operace nebo| , například: ios_base::out | ios_base::trunc - otevřete soubor pro zápis po jeho vymazání.

Objekty třídy ofstream , jsou-li spojeny se soubory, standardně obsahují režimy otevírání souborů ios_base::out | ios_base::trunc . To znamená, že soubor bude vytvořen, pokud neexistuje. Pokud soubor existuje, jeho obsah bude smazán a samotný soubor bude připraven k nahrávání. Objekty třídy ifstream, jsou-li spojeny se souborem, mají ve výchozím nastavení režim otevírání souboru ios_base::in - soubor je otevřen pouze pro čtení. Režimu otevírání souborů se také říká příznak, kvůli čitelnosti budeme tento termín v budoucnu používat. Tabulka 1 neuvádí všechny příznaky, ale pro začátek by vám měly stačit.

Vezměte prosím na vědomí, že příznaky ate a app jsou velmi podobné v popisu, oba přesouvají ukazatel na konec souboru, ale příznak aplikace umožňuje zápis pouze na konec souboru a příznak ate jednoduše přeuspořádá příznak na konec souboru a neomezuje prostor pro nahrávání.

Pojďme vyvinout program, který pomocí operace sizeof() vypočítá charakteristiky hlavních datových typů v C++ a zapíše je do souboru. Vlastnosti:

  1. počet bajtů přidělených pro datový typ
  2. maximální hodnota, kterou může konkrétní datový typ uložit.

Zápis do souboru musí být v následujícím formátu:

/* datový typ bajte maximální hodnota bool = 1 255,00 char = 1 255,00 krátký int = 2 32767,00 nepodepsaný krátký int = 2 65535,00 int = 4 2147483647,00 unsigned int = 4 4294900 = 429495447474747474747474747.00 PLAVIT = 429495447.00 PLAVA = 429495547.00 PLAVO dlouhý float = 8 9223372036854775800,00 double = 8 9223372036854775800,00 */

Takový program již byl vyvinut dříve v této části, ale tam byly všechny informace o datových typech vyvedeny na standardní výstupní zařízení a my musíme program předělat tak, aby se informace zapisovaly do souboru. Chcete-li to provést, musíte soubor otevřít v režimu zápisu s předběžným zkrácením informací o aktuálním souboru ( řádek 14). Jakmile je soubor vytvořen a úspěšně otevřen (řádky 16 - 20), místo příkazu cout v řádek 22 použijte objekt fout. tedy místo obrazovky budou informace o datových typech zapsány do souboru.

// write_file.cpp: definuje vstupní bod pro konzolovou aplikaci. #include "stdafx.h" #include #zahrnout // práce se soubory #include // I/O manipulátory používající jmenný prostor std; int main(int argc, char* argv) ( setlocale(LC_ALL, "rus"); // propojí objekt se souborem, přičemž soubor otevírá v režimu zápisu a nejprve z něj odstraní všechna data ze streamu fout("data_types.txt ", ios_base::out | ios_base::trunc); if (!fout.is_open()) // pokud soubor nebyl otevřen ( cout<< "Файл не может быть открыт или создан\n"; // напечатать соответствующее сообщение return 1; // выполнить выход из программы } fout << " data type " << "byte" << " " << " max value "<< endl // záhlaví sloupců <<"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; }

Není možné si nevšimnout, že změny v programu jsou minimální a to vše díky tomu, že standardní vstup/výstup a souborový vstup/výstup jsou využívány naprosto stejně. Na konci programuřádek 45soubor jsme výslovně uzavřeli, ačkoli to není vyžadováno, považuje se to za dobrou programátorskou praxi. Stojí za zmínku, že všechny funkce a manipulátory používané pro formátování standardního vstupu/výstupu jsou relevantní i pro vstup/výstup souboru. Při obsluze tedy nedošlo k žádným chybám cout byl nahrazen předmětem fout.

Poslední aktualizace: 31.10.2015

Dvě třídy jsou navrženy pro práci s adresáři v oboru názvů System.IO: Directory a DirectoryInfo .

Třída adresáře

Třída Directory poskytuje řadu statických metod pro správu adresářů. Některé z těchto metod jsou:

    CreateDirectory(cesta) : vytvoří adresář na zadané cestě

    Delete(path) : smaže adresář na dané cestě

    Existuje(cesta) : Určuje, zda adresář na zadané cestě existuje. Vrátí true, pokud existuje, false, pokud ne.

    GetDirectories(path) : Získá seznam adresářů v cestě

    GetFiles(path) : Získá seznam souborů v adresářové cestě

    Move(sourceDirName, destDirName): přesune adresář

    GetParent(path) : získání nadřazeného adresáře

Třída DirectoryInfo

Tato třída poskytuje funkce pro vytváření, odstraňování, přesouvání a další operace s adresáři. V mnoha ohledech je podobný adresáři. Některé z jeho vlastností a metod jsou:

    Create() : vytvoří adresář

    CreateSubdirectory(path) : vytvoří podadresář na zadané cestě

    Delete() : odstraní adresář

    Vlastnost Exists: určuje, zda adresář existuje

    GetDirectories() : Získá seznam adresářů

    GetFiles() : získání seznamu souborů

    MoveTo(destDirName) : přesune adresář

    Parent property: získání nadřazeného adresáře

    Kořenová vlastnost: získání kořenového adresáře

Podívejme se na příklady použití těchto tříd

Získání seznamu souborů a podadresářů

string dirName = "C:\\"; if (Directory.Exists(dirName)) ( Console.WriteLine("Subdirectories:"); string dirs = Directory.GetDirectories(dirName); foreach (řetězec s v dirs) ( Console.WriteLine(s); ) Console.WriteLine( ); Console.WriteLine("Files:"); string files = Directory.GetFiles(dirName); foreach (řetězce v souborech) ( Console.WriteLine(s); ) )

Všimněte si použití lomítek v názvech souborů. Buď použijeme dvojité lomítko: "C:\\" , nebo jednoduché, ale pak před celou cestu dáme znak @: @"C:\Program Files"

Vytvořte adresář

string path = @"C:\SomeDir"; podcesta řetězce = @"program\avalon"; DirectoryInfo dirInfo = new DirectoryInfo(cesta); if (!dirInfo.Exists) ( dirInfo.Create(); ) dirInfo.CreateSubdirectory(podcesta);

Nejprve zkontrolujeme, zda takový adresář existuje, protože pokud existuje, pak jej nebude možné vytvořit a aplikace vyhodí chybu. V důsledku toho získáme následující cestu: "C:\SomeDir\program\avalon"

Získání informací o adresáři

string dirName = "C:\\Program Files"; DirectoryInfo dirInfo = new DirectoryInfo(dirName); Console.WriteLine($"Název adresáře: (dirInfo.Name)"); Console.WriteLine($"Úplný název adresáře: (dirInfo.FullName)"); Console.WriteLine($"Čas vytvoření adresáře: (dirInfo.CreationTime)"); Console.WriteLine($"Kořenový adresář: (dirInfo.Root)");

Smazání adresáře

Pokud jednoduše aplikujeme metodu Delete na neprázdnou složku, která obsahuje nějaké soubory nebo podadresáře, pak aplikace vyhodí chybu. Proto musíme předat metodě Delete další parametr typu boolean, který bude indikovat, že složka by měla být odstraněna s celým jejím obsahem:

Řetězec dirName = @"C:\SomeFolder"; try ( DirectoryInfo dirInfo = new DirectoryInfo(dirName); dirInfo.Delete(true); Console.WriteLine("Adresář smazán"); ) catch (Exception ex) ( Console.WriteLine(ex.Message); )

Řetězec dirName = @"C:\SomeFolder"; Directory.Delete(dirName, true);

Přesunutí adresáře

string oldPath = @"C:\SomeFolder"; string newPath = @"C:\SomeDir"; DirectoryInfo dirInfo = new DirectoryInfo(oldPath); if (dirInfo.Exists && Directory.Exists(newPath) == false) ( dirInfo.MoveTo(newPath); )

Při přesouvání musíme počítat s tím, že nový adresář, do kterého chceme přesunout veškerý obsah starého adresáře, nesmí existovat.

Pro snadnou manipulaci jsou informace v úložných zařízeních ukládány ve formě souborů.

Soubor je pojmenovaná oblast externí paměti alokovaná pro ukládání pole dat. Data obsažená v souborech jsou nejrozmanitější povahy: programy v algoritmickém nebo strojovém jazyce; počáteční data pro provoz programů nebo výsledky provádění programu; libovolné texty; grafika atd.

Adresář (složka, adresář) - pojmenovaná kolekce bajtů na paměťovém médiu obsahující názvy podadresářů a souborů, používaná v souborovém systému pro zjednodušení organizace souborů.

souborový systém je funkční část operačního systému, která zajišťuje operace se soubory. Příklady souborových systémů jsou FAT (FAT - File Allocation Table, souborová alokační tabulka), NTFS, UDF (používané na CD).

Existují tři hlavní verze FAT: FAT12, FAT16 a FAT32. Liší se bitovostí záznamů ve struktuře disku, tzn. počet bitů přidělených pro uložení čísla clusteru. FAT12 se používá především pro diskety (do 4 KB), FAT16 pro malé disky, FAT32 pro velkokapacitní FLASH mechaniky (do 32 GB).

Zvažte strukturu systému souborů pomocí FAT32 jako příklad.

Struktura souborů FAT32

Externí paměťová zařízení v systému FAT32 nemají bajtové, ale blokové adresování. Informace se zapisují na externí paměťové zařízení v blocích nebo sektorech.

Sektor – minimální adresovatelná jednotka úložiště informací na externích paměťových zařízeních. Velikost sektoru je obvykle pevně nastavena na 512 bajtů. Pro zvětšení adresového prostoru externích paměťových zařízení jsou sektory kombinovány do skupin nazývaných clustery.

Klastr je sdružení několika sektorů, které lze považovat za samostatnou jednotku s určitými vlastnostmi. Hlavní vlastností clusteru je jeho velikost, měřená počtem sektorů nebo počtem bajtů.

Systém souborů FAT32 má následující strukturu.

Clustery používané pro zápis souborů jsou číslovány od 2. Klastr #2 je zpravidla používán kořenovým adresářem a počínaje clusterem #3 se ukládá datové pole. Sektory používané k ukládání informací nad kořenovým adresářem nejsou klastrované.
Minimální velikost souboru na disku je 1 cluster.

Spouštěcí sektor začíná následujícími informacemi:

  • EB 58 90 - nepodmíněná větev a podpis;
  • 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
  • 00 02 - počet bajtů v sektoru (obvykle 512);
  • 1 byte - počet sektorů v clusteru;
  • 2 bajty - počet rezervních sektorů.

Kromě toho spouštěcí sektor obsahuje následující důležité informace:

  • 0x10 (1 byte) – počet tabulek FAT (obvykle 2);
  • 0x20 (4 bajty) - počet sektorů na disku;
  • 0x2C (4 bajty) – číslo clusteru kořenového adresáře;
  • 0x47 (11 bajtů) – jmenovka svazku;
  • 0x1FE (2 bajty) - Podpis spouštěcího sektoru (55 AA).

Informační sektor souborového systému obsahuje:

  • 0x00 (4 bajty) – podpis (52 52 61 41 );
  • 0x1E4 (4 bajty) – podpis (72 72 41 61 );
  • 0x1E8 (4 bajty) – počet volných clusterů, -1 pokud není znám;
  • 0x1EC (4 bajty) – číslo posledního zaznamenaného clusteru;
  • 0x1FE (2 bajty) - podpis (55 AA).

Tabulka FAT obsahuje informace o stavu každého clusteru na disku. Spodní 2 bajty tabulky FAT ukládají F8 FF FF 0F FF FF FF FF (odpovídající stavu clusterů 0 a 1, fyzicky nepřítomné). Stav každého clusteru dále obsahuje číslo clusteru, ve kterém aktuální soubor pokračuje, nebo následující informace:

  • 00 00 00 00 – cluster je volný;
  • FF FF FF 0F je konec aktuálního souboru.
  • 8 bajtů - název souboru;
  • 3 bajty - přípona souboru;

Kořenový adresář obsahuje sadu 32bitových informačních záznamů pro každý soubor obsahující následující informace:

Při práci s dlouhými názvy souborů (včetně ruských názvů) je název souboru zakódován v kódovacím systému UTF-16. V tomto případě jsou pro kódování každého znaku přiděleny 2 bajty. V tomto případě je název souboru zapsán ve tvaru následující struktury:

  • 1 byte sekvence;
  • 10 bajtů obsahuje spodních 5 znaků názvu souboru;
  • atribut 1 byte;
  • 1 bajt vyhrazený;
  • 1 byte - kontrolní součet názvu DOS;
  • 12 bajtů obsahuje spodní 3 znaky názvu souboru;
  • 2 bajty – číslo prvního clusteru;
  • zbývající znaky dlouhého jména.

Práce se soubory v C

Pro programátora je otevřený soubor reprezentován jako sekvence dat, která jsou čtena nebo zapisována. Když je soubor otevřen, je spojen s I/O tok. Výstupní informace se zapisují do proudu, vstupní informace se z proudu čtou.

Když je proud otevřen pro I/O, je spojen se standardní strukturou typu FILE , která je definována v stdio.h . Struktura FILE obsahuje potřebné informace o souboru.

Otevření souboru se provádí pomocí funkce fopen(), která vrací ukazatel na strukturu typu FILE , kterou lze použít pro následné operace se souborem.

SOUBOR *fopen(jméno, typ);


name je název souboru, který se má otevřít (včetně cesty),
typ je ukazatel na řetězec znaků, které definují způsob přístupu k souboru:
  • "r" - otevřít soubor pro čtení (soubor musí existovat);
  • "w" - otevřete prázdný soubor pro zápis; pokud soubor existuje, jeho obsah je ztracen;
  • "a" - otevřít soubor pro zápis do konce (pro připojení); soubor je vytvořen, pokud neexistuje;
  • "r+" - otevřít soubor pro čtení a zápis (soubor musí existovat);
  • "w+" - otevře prázdný soubor pro čtení a zápis; pokud soubor existuje, jeho obsah je ztracen;
  • "a+" - otevřít soubor pro čtení a připojení, pokud soubor neexistuje, je vytvořen.

Vrácená hodnota je ukazatel na otevřený stream. Pokud je nalezena chyba, vrátí se NULL.

Funkce fclose() zavře proud nebo proudy spojené se soubory otevřenými pomocí fopen(). Proud, který má být uzavřen, je určen argumentem funkce fclose().

Návratová hodnota: hodnota 0, pokud byl proud úspěšně uzavřen; konstanta EOF, pokud došlo k chybě.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#zahrnout
int main() (
SOUBOR *fp;
název znaku = "my.txt" ;
if ((fp = fopen(jméno, "r" )) == NULL )
{
printf( "Nelze otevřít soubor");
getchar();
návrat 0;
}
// otevření souboru bylo úspěšné
... // požadované akce na datech
fclose(fp);
getchar();
návrat 0;
}

Čtení znaku ze souboru:

char fgetc(stream);


Argument funkce je ukazatel na proud typu FILE . Funkce vrací kód čteného znaku. Pokud je dosaženo konce souboru nebo dojde k chybě, je vrácena konstanta EOF.

Zápis znaku do souboru:

fputc(znak, proud);

Argumenty funkce jsou znak a ukazatel na proud typu FILE . Funkce vrací kód čteného znaku.

Funkce fscanf() a fprintf() jsou podobné funkcím scanf() a printf(), ale pracují s datovými soubory a jejich prvním argumentem je ukazatel na soubor.

fscanf(stream, "InputFormat" , args);