Työskentely tiedostojen kanssa C, syötä ja tulosta tiedostoon C. Syöttö tiedostosta ja tulostus tiedostoon Kuinka luodaan luettava tiedosto c

I/O-mekanismi, jonka on kehittänyt, ei vastaa nykyään yleisesti hyväksyttyä olioohjelmoinnin tyyliä, lisäksi se käyttää voimakkaasti osoitintoimintoja, joita pidetään mahdollisesti vaarallisina nykyaikaisissa suojatuissa koodin suoritusympäristöissä. Vaihtoehtona sovellussovelluksia kehitettäessä on C++-kielistandardin tarjoama standardi I/O-luokkien mekanismi.

Tiedostojen avaaminen

Yleisimmin käytetyt luokat ovat ifstream lukemiseen, ofstream kirjoittamiseen ja fstream tiedostojen muokkaamiseen.

Kaikki kierteitetyt I/O-luokat on johdettu epäsuorasti yhteisestä esi-iosista, ja ne perivät täysin sen toiminnallisuuden. Siten tiedoston avaustilan määrittää open_mode-luettelotyypin datajäsen, joka määritellään seuraavasti:

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

Alla on lippujen mahdolliset arvot ja niiden tarkoitus.

Jos haluat esimerkiksi avata tiedoston nimeltä test.txt lukeaksesi tietoja binäärimuodossa, kirjoitat:

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

Loogisen OR-operaattorin (|) avulla voit luoda tilan millä tahansa lippujen yhdistelmällä. Jotta et vahingossa korvaa olemassa olevaa tiedostoa samalla nimellä, kun avaat tiedoston syöttämällä, sinun on käytettävä seuraavaa lomaketta:

Offstream-tiedosto; file.open("test.txt", ios::out | ios::app);

Oletetaan, että vastaava otsikkotiedosto sisältyy projektiin:

#sisältää

Voit tarkistaa, onko tiedosto avattu onnistuneesti, käyttämällä konstruktia

If (!file) ( //Tiedoston avausvirhe)

Inkluusio- ja louhintaoperaattorit

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

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

Voit myös kirjoittaa tekstijonon osissa:

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

Endl-lause päättää rivin syötteen rivinvaihdolla:

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

Sisällytäoperaattorilla on helppo kirjoittaa muuttujien tai taulukon elementtien arvot tiedostoon:

Offstream-tiedosto ("Temp.txt"); char buff = "Tekstitaulukko sisältää muuttujia"; int vx = 100; float pi = 3,14159; tiedosto<< buff << endl << vx << endl << pi << endl;

Koodin suorittamisen tuloksena tekstitiedostosta Temp.txt muodostuu kolme riviä:

Tekstitaulukko sisältää muuttujat 100 3.14159

Huomaa, että numeeriset arvot kirjoitetaan tiedostoon tekstijonoina binääriarvojen sijaan.

Hakuoperaattori(>>) tuottaa päinvastaisen vaikutuksen. Vaikuttaa siltä, ​​että jos haluat purkaa merkkejä aiemmin kirjoitetusta Temp.txt-tiedostosta, kirjoitat seuraavanlaisen koodin:

Ifstream-tiedosto ("Temp.txt"); char buff; int vx; kellua pi; tiedosto >> buff >> vx >> pi;

Poimintaoperaattori kuitenkin pysähtyy ensimmäiseen kohtaamaansa erottimeen (välilyönti, sarkain tai rivinvaihto). Näin ollen kun jäsennetään lausetta "Tekstitaulukko sisältää muuttujia", vain sana "Teksti" kirjoitetaan buff-taulukkoon, välilyönti jätetään huomioimatta ja sanasta "array" tulee koko vx-muuttujan arvo ja koodi. suoritus "menee pieleen" ja väistämättä rikkoo tietorakennetta. Seuraavaksi, kun keskustelemme ifstream-luokasta, näytämme kuinka järjestää tiedoston lukeminen oikein edellisestä esimerkistä.

ifstream-luokka: tiedostojen lukeminen

Kuten nimestä voi päätellä, ifstream-luokka on suunniteltu syöttämään tiedostovirta. Luokan päämenetelmät on lueteltu alla. Suurin osa niistä on peritty istream-luokasta ja ylikuormitettu vanhempien toimintojen laajentamiseksi. Esimerkiksi get-funktio voi puheluparametrista riippuen lukea yksittäisen merkin lisäksi myös merkkilohkon.

Nyt on selvää, kuinka edellistä esimerkkiä on muutettava niin, että tiedonpoimintaoperaattorin käyttö antaa odotetun tuloksen:

Ifstream-tiedosto ("Temp.txt"); char buff; int vx; kellua pi; file.getline(buff, sizeof(buff)); tiedosto >> vx >> pi:

Getline-menetelmä lukee tiedoston ensimmäisen rivin loppuun ja >>-operaattori antaa muuttujille arvot.

Seuraava esimerkki näyttää tietojen lisäämisen tekstitiedostoon ja sitten koko tiedoston lukemisen. While(1)-silmukkaa käytetään while(!file2.eof()):n sijaan julkaisussa käsitellyistä syistä.

#sisältää #sisältää käyttäen nimiavaruutta std; int main() ( offstream-tiedosto; 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; }

Seuraava esimerkki näyttää silmukan, joka lukee rivejä test.txt-tiedostosta ja näyttää ne konsolissa.

#sisältää #sisältää käyttäen nimiavaruutta std; int main() ( ifstream-tiedosto; // luo stream-objektitiedosto file.open("test.txt"); // avaa tiedosto luettavaksi if (!file) return 1; // paluu avattaessa virhe char str; // staattinen rivipuskuri // Lue ja näytä rivejä silmukassa, kunnes eof while (!file.getline(str, sizeof(str)).eof()) cout<< str << endl; // вывод прочитанной строки на экран cin.sync(); cin.get(); return 0; }

Tämä koodi Windows-käyttöjärjestelmässä riippuu myös rivinvaihdosta tiedoston viimeisellä rivillä, olisi luotettavampaa tehdä tämä:

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

Avaa- ja sulkemismenetelmien nimenomaisia ​​kutsuja ei vaadita. Itse asiassa rakentajan kutsuminen argumentilla mahdollistaa tiedoston avaamisen heti, kun säikeitettyä tiedostoobjektia luodaan:

Ifstream-tiedosto("test.txt");

Sulje-menetelmän sijaan voit käyttää delete-operaattoria, joka kutsuu automaattisesti tiedostoobjektin destructorin ja sulkee tiedoston. While-silmukkakoodi varmistaa asianmukaiset tiedoston lopun tarkistukset.

offstream-luokka: tiedostojen kirjoittaminen

Offstream-luokka on suunniteltu tuottamaan dataa tiedostovirrasta. Seuraavassa luetellaan tämän luokan päämenetelmät.

Aiemmin kuvattu inkluusiooperaattori sopii kätevästi tekstitiedostoon kirjoittamisen järjestämiseen:

Offstream-tiedosto("temp.txt"); if (!tiedosto) return; for (int i=1; i<=3; i++) file << "Строка " << i << endl; file.close();

Binaaritiedostot

Periaatteessa binaaridataa käsitellään kuin tekstidataa. Erona on se, että jos binääridataa kirjoitetaan tiettyyn loogiseen rakenteeseen, se on luettava tiedostosta saman rakennetyypin muuttujaan.

Kirjoitus- ja lukumenetelmien ensimmäisen parametrin (kirjoitus/lukulohkon osoitteen) tulee olla merkkiosoitintyyppiä char * , joten void * -rakenteen osoitetyypin eksplisiittinen muunnos on välttämätöntä. Toinen parametri määrittää, että tiedoston binäärilohkoilla on vakio tavukoko riippumatta tietueen todellisesta pituudesta. Seuraava sovellus tarjoaa esimerkin tietojen luomisesta ja näyttämisestä yksinkertaisessa muistikirjassa. Tiedostomerkinnät luetaan sitten peräkkäin ja näytetään konsolissa.

#sisältää #sisältää #sisältää käyttäen nimiavaruutta std; struct Notes ( // muistikirjan tietorakenne char Nimi; // koko nimi char Puhelin; // puhelin int Ikä; // ikä ); int main() ( setlocale(LC_ALL, "venäläinen"); Notes Note1= ("Kauhea Ioann Vasilyevich", "ei asennettu", 60 ); Notes Note2= ("Godunov Boris Fedorovich", "095-111-2233" , 30 Note3= ("Romanov Petr Mikhailovich", "812-333-2211", 20 ) ofstream("Notebook.dat", ios::binary.write((char*)&Note1,); sizeof (Notes)); // 1st block ofile.write((char*)&Note2, sizeof(Notes) // 2nd block ofile.write((char*)&Note3, sizeof(Notes)); .close(); (!file.read((char*)&Note, sizeof(Notes)).eof()) ( sprintf(str, "%s\tPuhelin: %s\tIkä: %d" , Note.Name, Note.Phone, Huomautus.Ikä);<< str << endl; } ifile.close(); // закрыть прочитанный файл cin.sync(); cin.get(); return 0; }

Tämän koodin suorittamisen tuloksena binääritiedosto Notebook.dat muodostetaan kolmesta 80 tavun lohkosta (olettaen, että merkit ovat yksitavuisia). Tietysti voit käyttää muita lankamenetelmiä ja suorittaa mitä tahansa toimintoja tietyn tietorakenteen kentille.

fstream-luokka: satunnainen tiedostopääsy

Oletetaan, että muistikirjassamme on 100 merkintää ja haluamme laskea 50:nnen. Tietenkin voit järjestää silmukan ja lukea kaikki tietueet ensimmäisestä annettuun. Ilmeisesti kohdennettumpi ratkaisu on asettaa pos-tiedoston sijaintiosoitin suoraan merkintään 50 ja lukea siitä:

Ifstream ifile("Notebook.dat", ios::binary); int pos = 49 * sizeof(Notes); ifile.seekg(pos); // etsi 50th Notes -merkintä; //Notes – edellä kuvattu "tietue"-rakenne ifile.read((char*)&Note, sizeof(Notes));

Tällaiset hakutoiminnot ovat tehokkaita, jos tiedosto koostuu tunnetun ja vakiokokoisista tietueista. Jos haluat korvata mielivaltaisen tietueen sisällön, sinun on avattava lähtövirta muokkaustilassa:

Offstream ofile ("Notebook.dat", ios::binary | ios::ate); int pos = 49 * sizeof(Notes); ofile seekp(pos); // etsi 50. nuotti Notes Note50 = ("Jeltsin Boris Nikolaevich", "095-222-3322", 64); ofile.write((char*)&Note, sizeof(Notes)); // korvaus

Jos et määritä ios::ate (tai ios::app) -lippua, kun avaat Notebook.dat-binaaritiedoston, sen aiempi sisältö poistetaan!

Lopuksi on mahdollista avata tiedosto samanaikaisesti lukemista/kirjoittamista varten käyttämällä menetelmiä, jotka fstream-suoratoistoluokka on perinyt edeltäjiltään. Koska fstream-luokka on johdettu istreamista ja ostreamista (ifstreamin ja vastaavasti ofstreamin vanhemmat), kaikki aiemmin mainitut menetelmät tulevat saataville sovelluksessa.

Seuraava esimerkki näyttää ensimmäisen ja kolmannen merkinnän uudelleenjärjestelyn Notebook.dat-tiedostossa.

#sisältää #sisältää #sisältää käyttäen nimiavaruutta std; struct Muistiinpanot (char Nimi; char Puhelin; int Ikä; ); int main() ( setlocale(LC_ALL, "venäläinen"); Notes Note1, Note3; // Avaa tiedosto lukemista/kirjoittamista varten samanaikaisesti fstream file("Notebook.dat", ios::binary | ios::in | ios: : out file.seekg(2 * sizeof(Notes)); // etsi ja lue Note3 file.read((char*)&Note3, sizeof(Notes) // etsi ja lue Note1 file.read((char *)&Note1, sizeof(Notes)); file.seekg(0);<== 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; }

Tiedostoobjektin rakentajassa on määritettävä ios::in- ja ios::out-liput, jotka mahdollistavat samanaikaiset luku- ja kirjoitustoiminnot. Tämän koodin suorittamisen seurauksena Notebook.dat-binaaritiedoston ensimmäinen ja kolmas merkintä vaihdetaan.

Aiheesta löytyy lisää esimerkkejä.

Aiemmin, kun syötimme ja tulostamme tietoja, työskentelimme vakiovirtojen - näppäimistön ja näytön - kanssa. Katsotaan nyt kuinka C-kieli toteuttaa tietojen vastaanottamisen tiedostoista ja kirjoittamisen sinne. Ennen kuin voit suorittaa näitä toimintoja, sinun on avattava tiedosto ja käytettävä sitä.

C-ohjelmointikielessä osoitin tiedostoon on tyyppiä FILE ja sen ilmoitus näyttää tältä:
FILE *omatiedosto;

Toisaalta fopen()-funktio avaa tiedoston sen ensimmäiseksi argumentiksi määritetyssä osoitteessa lukutilassa ("r"), kirjoitustilassa ("w") tai lisäystilassa ("a") ja palauttaa osoittimen. siihen ohjelmaan. Siksi tiedoston avaaminen ja sen yhdistäminen ohjelmaan näyttää tältä:
oma tiedosto = fopen("hello.txt", "r");

Kun luet tai kirjoitat tietoja tiedostoon, siihen päästään tiedostoosoittimen (tässä tapauksessa myfile) kautta.

Jos syystä tai toisesta (määritetyssä osoitteessa ei ole tiedostoa, pääsy siihen on estetty) fopen()-funktio ei voi avata tiedostoa, se palauttaa NULL-arvon. Oikeissa ohjelmissa ne käsittelevät lähes aina tiedoston avaamisvirheen if-haarassa, mutta jätämme tämän pois.

Fopen()-funktion ilmoitus sisältyy stdio.h-otsikkotiedostoon, joten se on sisällytettävä. Myös stdio.h:ssa on ilmoitettu rakennetyyppi FILE.

Kun tiedoston kanssa työskentely on valmis, se on tapana sulkea puskurin vapauttamiseksi tiedoista ja muista syistä. Tämä on erityisen tärkeää, jos ohjelma jatkaa toimintaansa tiedoston käsittelyn jälkeen. Ulkoisen tiedoston ja ohjelmasta siihen johtavan osoittimen välinen yhteys katkaistaan ​​fclose()-funktiolla. Osoitin tiedostoon välitetään sille parametrina:
fclose(omatiedosto);

Ohjelmassa voidaan avata useampi kuin yksi tiedosto. Tässä tapauksessa jokaiseen tiedostoon on liitettävä oma tiedostoosoitin. Jos ohjelma kuitenkin ensin toimii yhden tiedoston kanssa ja sulkee sen sitten, osoitinta voidaan käyttää toisen tiedoston avaamiseen.

Tekstitiedostosta lukeminen ja siihen kirjoittaminen

fscanf()

Fscanf()-funktio on merkitykseltään samanlainen kuin scanf()-funktio, mutta toisin kuin se, se tarjoaa muotoillun syötteen tiedostosta tavallisen syöttövirran sijaan. Fscanf()-funktio ottaa parametrit: tiedostoosoitin, muotomerkkijono, muistialueiden osoitteet tietojen kirjoittamista varten:
fscanf(omatiedosto, "%s%d", str, &a);

Palauttaa onnistuneesti luettujen tietojen tai EOF:n määrän. Välilyönnit ja rivinvaihtomerkit lasketaan datan erottimiksi.

Oletetaan, että meillä on tiedosto, joka sisältää seuraavan kuvauksen objekteista:

Omenat 10 23,4 banaanit 5 25,0 leipää 1 10,3

#sisältää main () ( FILE * tiedosto; struct food ( merkin nimi[ 20 ] ; allekirjoittamaton määrä; kelluva hinta; ) ; struct food shop[ 10 ] ; char i= 0 ; file = fopen ( "fscanf.txt" , "r" ) ; while (fscanf (tiedosto, "%s%u%f" , shop[ i].name , & (shop[ i].qty ) , & (shop[ i].price ) != EOF) ( printf ( "%s %u %.2f \n", kauppa[ i].nimi, kauppa[ i].määrä, kauppa[ i].hinta) ; i++; ) )

Tässä tapauksessa kerrotaan rakenne ja joukko rakenteita. Jokainen tiedoston rivi vastaa yhtä taulukon elementtiä; taulukkoelementti on rakenne, joka sisältää merkkijonon ja kaksi numeerista kenttää. Silmukka lukee yhden rivin iteraatiota kohden. Kun tiedoston loppu havaitaan, fscanf() palauttaa EOF:n ja silmukka päättyy.

fgets()

Fgets()-funktio on samanlainen kuin gets()-funktio ja suorittaa rivi riviltä syötteen tiedostosta. Yksi fgets()-kutsu lukee yhden rivin. Tässä tapauksessa et voi lukea koko riviä, vaan vain osaa siitä alusta. fgets()-parametrit näyttävät tältä:
fgets (merkkijono, luettujen_merkkien määrä, osoitin tiedostoon)

Esimerkiksi:
fgets(str, 50, oma tiedosto)

Tämä funktiokutsu lukee myfile-osoittimeen liittyvästä tiedostosta yhden kokonaisen tekstirivin, jos sen pituus on alle 50 merkkiä, mukaan lukien "\n"-merkki, jonka funktio myös tallentaa taulukkoon. Str-taulukon viimeinen (50.) elementti on "\0"-merkki, jonka lisää fgets() . Jos merkkijono on pidempi, funktio lukee 49 merkkiä ja kirjoittaa loppuun "\0". Tässä tapauksessa "\n" ei sisälly lukuriville.

#sisältää #define N 80 main () ( TIEDOSTO * tiedosto; char arr[ N] ; file = fopen ( "fscanf.txt" , "r" ) ; while (fgets (arr, N, file) != NULL) printf (" %s" , arr) ; printf (" \n") ; fclose(tiedosto); )

Tässä ohjelmassa, toisin kuin edellisessä, tiedot luetaan rivi riviltä arr-taulukkoon. Kun seuraava rivi luetaan, edellinen menetetään. Fgets()-funktio palauttaa NULL-arvon, jos se ei pysty lukemaan seuraavaa riviä.

getc() tai fgetc()

Funktiolla getc() tai fgetc() (molemmat toimivat) voit saada seuraavan yksittäisen merkin tiedostosta.

while ((arr[ i] = fgetc (tiedosto) != EOF) ( if (arr[ i] == " \n") (arr[i] = " \0 " ; printf("%s \n", arr) ; i = 0; ) muuten i++; )arr[i] = " \0 " ; printf("%s \n", arr) ;

Esimerkkikoodi näyttää tiedot tiedostosta näytöllä.

Tekstitiedostoon kirjoittaminen

Aivan kuten syöttö, tulostus tiedostoon voi olla erilainen.

  • Muotoiltu tulos. Funktio fprintf (tiedoston_indeksi, muotomerkkijono, muuttujat) .
  • Post-by-line -tulostus. Funktio fputs(merkkijono, tiedosto_osoitin) .
  • Merkki merkiltä tulos. Funktio fputc() tai putc(symboli, tiedosto_osoitin) .

Alla on esimerkkejä koodista, jotka käyttävät kolmea menetelmää tietojen tulostamiseen tiedostoon.

Yhden rakenteen kenttien kirjoittaminen tiedoston jokaiselle riville:

tiedosto = fopen ("fprintf.txt" , "w" ) ; while (scanf ("%s%u%f" , shop[ i].name , & (shop[ i].qty ) , & (shop[ i].price ) ) != EOF) ( fprintf (tiedosto, " %s %u %.2f \n", kauppa[ i].nimi, kauppa[ i].määrä, kauppa[ i].hinta) ; i++; )

Rivi riviltä tuloste tiedostoon (fputs(), toisin kuin puts() itse, ei sijoita "\n" rivin loppuun):

while (gets (arr) != NULL) ( fputs (arr, tiedosto); fputs (" \n", tiedosto); )

Esimerkki merkkikohtaisesta tulostuksesta:

while ((i = getchar () ) != EOF) putc (i, tiedosto) ;

Binääritiedostosta lukeminen ja siihen kirjoittaminen

Voit työskennellä tiedoston kanssa ei merkkijonona, vaan tavusarjana. Periaatteessa ei ole mahdollista käsitellä muita kuin tekstitiedostoja. Voit kuitenkin lukea ja kirjoittaa tekstitiedostoja tällä tavalla. Tämän tiedoston käyttötavan etuna on luku-kirjoitusnopeus: merkittävä tietolohko voidaan lukea/kirjoittaa yhdellä pääsyllä.

Kun tiedosto avataan binäärikäyttöä varten, toinen parametri fopen():lle on merkkijono "rb" tai "wb".

Binääritiedostojen kanssa työskentelyn aihe on melko monimutkainen ja vaatii erillisen oppitunnin sen tutkimiseksi. Tässä huomioidaan vain tavuvirtana pidettävän tiedoston luku- ja kirjoitustoimintojen ominaisuudet.

Fread()- ja fwrite()-funktiot ovat parametreja:

  1. sen muistialueen osoite, josta tietoja kirjoitetaan tai luetaan,
  2. minkä tahansa tyypin koko,
  3. määrätyn kokoisen luetun datan määrä,
  4. tiedostohakemisto.

Nämä funktiot palauttavat onnistuneesti luettujen tai kirjoitettujen tietojen määrän. Nuo. Voit "tilata" 50 tietoelementin lukemisen, mutta vastaanottaa vain 10. Virheitä ei tapahdu.

Esimerkki fread()- ja fwrite()-funktioiden käytöstä:

#sisältää #sisältää main () ( FILE * tiedosto; char shelf1[ 50 ], shelf2[ 100 ] ; int n, m; file = fopen ("hylly1.txt" , "rb" ) ; n= fread (hylly1, koko (char ) , 50 , file) ; fclose (tiedosto) ; \0 " ; hylly2[m] = " \n"; hylly2[ m+ 1 ] = " \0 " ; tiedosto = fopen ("shop.txt" , "wb" ) ; fwrite (strcat (hylly2, hylly1) , koko (char ) , n+ m, tiedosto) ; fclose(tiedosto); )

Tässä yritetään lukea 50 merkkiä ensimmäisestä tiedostosta. n tallentaa todellisuudessa luettujen merkkien määrän. N:n arvo voi olla 50 tai vähemmän. Tiedot asetetaan riville. Sama tapahtuu toisen tiedoston kanssa. Seuraavaksi ensimmäinen rivi liitetään toiseen ja tiedot siirretään kolmanteen tiedostoon.

Ongelmanratkaisu

  1. Kirjoita ohjelma, joka kysyy käyttäjältä tekstitiedoston nimen (osoitteen), avaa sen ja laskee siinä olevien merkkien ja rivien määrän.
  2. Kirjoita ohjelma, joka kirjoittaa tiedostoon toisesta tiedostosta saatuja ja jollain tavalla muokattuja tietoja ennen kirjoittamista. Jokaisen tiedostosta saadun tietorivin tulee sopia rakenteeseen.

Tiedoston I/O C++:ssa toimii lähes identtisesti tavallisen I/O:n kanssa (mutta muutamalla pienellä erolla).

Tiedosto I/O-luokat

Syödä kolme päätiedoston I/O-luokkaa C++:ssa:

virran ulkopuolella(on luokan lapsi);

fstream(on iostream-luokan lapsi).

Näitä luokkia käyttämällä voit suorittaa yksisuuntaisen tiedostosyötön, yksisuuntaisen tiedostotulostuksen ja kaksisuuntaisen tiedoston I/O:n. Käyttääksesi niitä sinun tarvitsee vain yhdistää fstream.

Toisin kuin cout-, cin-, cerr- ja clog-virrat, joita voidaan käyttää välittömästi, ohjelmoijan on asennettava tiedostovirrat erikseen. Toisin sanoen tiedoston avaamiseksi lukemista ja/tai kirjoittamista varten on luotava sopiva tiedosto I/O-luokkaan kuuluva objekti ja määritettävä parametrina tiedoston nimi. Sitten käyttämällä lisäysoperaattoreita (<<) или извлечения (>>), voit kirjoittaa tietoja tiedostoon tai lukea tiedoston sisältöä. Tämän jälkeen viimeinen asia on sulkea tiedosto: explicitly call close() -menetelmä tai anna tiedoston I/O-muuttujan mennä soveltamisalan ulkopuolelle (tiedoston I/O-luokka sulkee tiedoston automaattisesti puolestamme).

Tiedoston tulos

Tiedostoon kirjoittamiseen käytetään ofstream-luokkaa. Esimerkiksi:

#sisältää #sisältää #sisältää // käyttääksesi exit() int main() ( käyttämällä nimiavaruutta std; // ofstreamia käytetään tietojen kirjoittamiseen tiedostoon // Luo tiedosto SomeText.txt ofstream outf("SomeText.txt"); // Jos voimme 'älä avaa tätä tiedostoa tietojen kirjoittamista varten if (!outf) ( // Näytä sitten virheilmoitus ja suorita 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 автоматически закроет наш файл }

#sisältää

#sisältää

#sisältää // käyttääksesi exit()

int main()

käyttäen nimiavaruutta std ;

// ofstream -toimintoa käytetään tietojen kirjoittamiseen tiedostoon

// Luo tiedosto SomeText.txt

ofstream outf("SomeText.txt");

// Jos emme voi avata tätä tiedostoa kirjoittaaksemme siihen tietoja

jos (! outf )

// Sitten näytämme virheilmoituksen ja suoritamme exit()

cerr<< << endl ;

poistu(1);

// Kirjoita tiedostoon seuraavat kaksi riviä

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

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

paluu 0;

// Kun outf menee soveltamisalan ulkopuolelle, ofstream-luokan tuhoaja sulkee tiedostomme automaattisesti

Jos katsot projektisi hakemistosta ( Napsauta hiiren kakkospainikkeella välilehteä, jossa on .cpp-tiedostosi nimi Visual Studiossa > "Avaa sisältävä kansio"), näet tiedoston nimeltä SomeText.txt, joka sisältää seuraavat rivit:

Katso rivi #1!
Katso rivi #2!

Huomaa, että voimme myös käyttää put() -menetelmä kirjoittaaksesi tiedostoon yhden merkin.

Tiedoston syöttö

#sisältää #sisältää #sisältää #sisältää // käyttääksesi exit() int main() ( käyttäen nimiavaruutta std; // ifstreamiä käytetään tiedoston sisällön lukemiseen // Yritetään lukea tiedoston SomeText.txt sisältö ifstream inf("SomeText.txt" ); // Jos emme voi avata tätä tiedostoa lukeaksemme sen sisältöä 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; }

#sisältää

#sisältää

#sisältää

#sisältää // käyttääksesi exit()

int main()

käyttäen nimiavaruutta std ;

// ifstreamiä käytetään tiedoston sisällön lukemiseen

// Jos emme voi avata tätä tiedostoa lukeaksemme sen sisältöä

jos (! inf )

// Sitten tulostamme seuraavan virheilmoituksen ja suoritamme exit()

cerr<< << endl ;

poistu(1);

// Niin kauan kuin on dataa, jota voimme lukea

kun (inf)

// Sitten siirrämme nämä tiedot riville, jonka sitten näytämme näytöllä

merkkijono strInput ;

inf >> strInput ;

cout<< strInput << endl ;

paluu 0;

// Kun inf menee soveltamisalan ulkopuolelle, ifstream-luokan tuhoaja sulkee tiedostomme automaattisesti

Katso
linja
#1!
Katso
linja
#2!

Hmm, sitä emme aivan halunneet. Kuten tiedämme jo aiemmilta oppitunneilta, poimintaoperaattori toimii "muotoillun datan" kanssa, ts. se jättää huomioimatta kaikki välilyönnit, sarkaimet ja rivinvaihdot. Jotta voisimme lukea kaiken sisällön sellaisenaan jakamatta sitä osiin (kuten yllä olevassa esimerkissä), meidän on käytettävä getline() -menetelmä:

#sisältää #sisältää #sisältää #sisältää // käyttääksesi exit() int main() ( käyttäen nimiavaruutta std; // ifstreamiä käytetään tiedostojen sisällön lukemiseen // Yritämme lukea tiedoston SomeText.txt sisällön ifstream inf("SomeText.txt" // Jos emme voi avata tiedostoa lukeaksemme sen sisältöä if (!inf) ( // Tulosta sitten seuraava virheilmoitus ja suorita 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 автоматически закроет наш файл }

#sisältää

#sisältää

#sisältää

#sisältää // käyttääksesi exit()

int main()

käyttäen nimiavaruutta std ;

// ifstreamiä käytetään tiedostojen sisällön lukemiseen

ifstream inf ("SomeText.txt" ) ;

// Jos emme voi avata tiedostoa lukeaksemme sen sisältöä

jos (! inf )

// Sitten tulostamme seuraavan virheilmoituksen ja suoritamme exit()

cerr<< "Voi, SomeText.txt-tiedostoa ei voitu avata lukemista varten!"<< endl ;

poistu(1);

kun (inf)

merkkijono strInput ;

getline(inf, strInput);

cout<< strInput << endl ;

paluu 0;

// Kun inf menee soveltamisalan ulkopuolelle, ifstream-luokan tuhoaja sulkee tiedostomme automaattisesti

Yllä olevan ohjelman tulos:

Puskuroitu lähtö

C++:n lähtö voidaan puskuroida. Tämä tarkoittaa, että kaikkea, mikä tulostetaan tiedostovirtaan, ei voida kirjoittaa välittömästi levylle (tiettyyn tiedostoon). Tämä tehdään ensisijaisesti suorituskykysyistä. Kun puskuritiedot kirjoitetaan levylle, sitä kutsutaan puskurin tyhjennys. Yksi tapa tyhjentää puskuri on sulkea tiedosto. Tässä tapauksessa puskurin koko sisältö siirretään levylle ja sitten tiedosto suljetaan.

Lähtöpuskurointi ei yleensä ole ongelma, mutta tietyissä olosuhteissa se voi aiheuttaa ongelmia varomattomille aloittelijoille. Esimerkiksi kun tiedot tallennetaan puskuriin ja ohjelma päättyy ennenaikaisesti (joko vian seurauksena tai kutsumalla ). Tällaisissa tapauksissa tiedostojen I/O-luokan tuhoajia ei suoriteta, tiedostoja ei koskaan suljeta, puskureita ei tyhjennetä ja tietomme katoavat ikuisesti. Tästä syystä on hyvä idea sulkea kaikki avoimet tiedostot ennen exit(:n) kutsumista.

Voit myös tyhjentää puskurin manuaalisesti käyttämällä ostream::flush() -menetelmä tai lähettämällä std::flush lähtövirtaan. Kumpikin näistä menetelmistä voi olla hyödyllinen sen varmistamiseksi, että puskurin sisältö kirjoitetaan välittömästi levylle, jos ohjelma epäonnistuu.

Mielenkiintoinen vivahde: Koska std::endl; tyhjentää myös lähtövirran, sen liiallinen käyttö (joka johtaa tarpeettomiin puskurin tyhjennyksiin) voi vaikuttaa ohjelman suorituskykyyn (koska puskurin huuhtelu voi joissain tapauksissa olla kallis toimenpide). Tästä syystä ohjelmoijat, jotka välittävät koodinsa suorituskyvystä, käyttävät usein \n-merkkiä std::endl:n sijasta rivinvaihdon lisäämiseksi tulosvirtaan välttääkseen tarpeettoman puskurin huuhtelun.

Tiedoston avaustilat

Mitä tapahtuu, jos yritämme kirjoittaa tietoja jo olemassa olevaan tiedostoon? Yllä olevan ohjelman suorittaminen uudelleen (ensimmäinen) osoittaa, että alkuperäinen tiedosto kirjoitetaan kokonaan päälle, kun suoritat ohjelman uudelleen. Entä jos meidän on liitettävä tietoja tiedoston loppuun? Osoittautuu, että tiedostovirta ottaa valinnaisen toisen parametrin, jonka avulla voit kertoa ohjelmoijalle, kuinka tiedosto avataan. Voit välittää tämän parametrin muodossa seuraavat liput(jotka ovat ios-luokassa):

sovellus- avaa tiedoston liitetilassa;

söi- menee tiedoston loppuun ennen lukemista/kirjoittamista;

binääri- avaa tiedoston binääritilassa (tekstitilan sijaan);

sisään- avaa tiedoston lukutilassa (oletus ifstreamille);

ulos- avaa tiedoston tallennustilassa (oletus on offstream);

trunc- poistaa tiedoston, jos se on jo olemassa.

Voit määrittää useita lippuja kerralla käyttämällä .

ifstream toimii oletuksena ios::in tilassa;

ofstream toimii oletusarvoisesti ios::out-tilassa;

fstream toimii oletuksena ios::in TAI ios::out -tilassa, mikä tarkoittaa, että voit joko lukea tiedoston sisällön tai kirjoittaa tietoja tiedostoon.

Kirjoita nyt ohjelma, joka lisää kaksi riviä aiemmin luotuun SomeText.txt-tiedostoon:

#sisältää #sisältää // käyttääksesi exit() #include int main() ( käyttäen nimiavaruutta std; // Välitä ios:app-lippu ilmoittaaksesi fstreamille, että aiomme liittää tietomme tiedoston olemassa oleviin tietoihin, // emme aio korvata tiedostoa. Emme tee sitä. t tarvitse välittää ios::out-lippu , // koska ofstream toimii oletusarvoisesti ios::out-tilassa ofstream outf("SomeText.txt", ios::app // Jos emme voi avata tiedostoa tietojen kirjoittamista varten). if (!outf) ( // Tulosta sitten seuraava virhesanoma ja suorita 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 автоматически закроет наш файл }

#sisältää

#sisältää // käyttääksesi exit()

#sisältää

int main()

käyttäen nimiavaruutta std ;

// Välitä ios:app-lippu ilmoittaaksesi fstreamille, että aiomme liittää tietomme olemassa oleviin tiedostotietoihin,

// Emme aio ylikirjoittaa tiedostoa. Meidän ei tarvitse ohittaa ios::out -lippua,

// koska ofstream toimii oletusarvoisesti ios::out-tilassa

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

// Jos emme voi avata tiedostoa tietojen kirjoittamista varten

jos (! outf )

// Sitten tulostamme seuraavan virheilmoituksen ja suoritamme exit()

cerr<< "Voi, SomeText.txt-tiedostoa ei voitu avata kirjoittamista varten!"<< endl ;

poistu(1);

Käytön helpottamiseksi tallennuslaitteissa olevat tiedot tallennetaan tiedostoina.

Tiedosto on nimetty ulkoisen muistin alue, joka on varattu datajoukon tallentamiseen. Tiedostojen sisältämä data on luonteeltaan hyvin monipuolista: ohjelmia algoritmi- tai konekielellä; ohjelman toiminnan tai ohjelman suoritustulosten alkutiedot; ilmaiset tekstit; graafisia kuvia jne.

Hakemisto (kansio, hakemisto) - nimetty tavukokoelma tallennusvälineellä, joka sisältää alihakemistojen ja tiedostojen nimet ja jota käytetään tiedostojärjestelmässä tiedostojen järjestämisen yksinkertaistamiseksi.

Tiedostojärjestelmä kutsutaan käyttöjärjestelmän toiminnalliseksi osaksi, joka suorittaa toimintoja tiedostoille. Esimerkkejä tiedostojärjestelmistä ovat FAT (FAT - File Allocation Table), NTFS, UDF (käytetään CD-levyillä).

FATista on kolme pääversiota: FAT12, FAT16 ja FAT32. Ne eroavat levyrakenteen tietueiden bittisyvyydestä, ts. klusterin numeron tallentamiseen allokoitujen bittien lukumäärä. FAT12:ta käytetään pääasiassa levykkeille (enintään 4 KB), FAT16 - pienikapasiteettiisille levyille, FAT32 - suurikapasiteettisille FLASH-asemille (jopa 32 Gt).

Katsotaanpa tiedostojärjestelmän rakennetta käyttämällä esimerkkinä FAT32:ta.

FAT32-tiedostorakenne

FAT32-järjestelmän ulkoisissa muistilaitteissa on lohkoosoitus tavuosoitteiden sijaan. Tiedot kirjoitetaan ulkoiseen muistilaitteeseen lohkoissa tai sektoreissa.

Sektori on pienin osoitettavissa oleva tiedon tallennusyksikkö ulkoisille tallennuslaitteille. Tyypillisesti sektorin koko on kiinteä 512 tavua. Ulkoisten muistilaitteiden osoiteavaruuden lisäämiseksi sektorit yhdistetään ryhmiksi, joita kutsutaan klustereiksi.

Klusteri on useiden sektoreiden liitto, jota voidaan pitää itsenäisenä yksikkönä, jolla on tietyt ominaisuudet. Klusterin pääominaisuus on sen koko, mitattuna sektorien tai tavujen lukumääränä.

FAT32-tiedostojärjestelmällä on seuraava rakenne.

Tiedostojen kirjoittamiseen käytettävät klusterit on numeroitu alkaen 2:sta. Pääsääntöisesti klusteria nro 2 käyttää juurihakemisto ja klusterista 3 alkaen tietotaulukko tallennetaan. Sektoreita, joita käytetään tietojen tallentamiseen juurihakemiston yläpuolelle, ei ole ryhmitelty.
Levyllä vaadittava vähimmäistiedostokoko vastaa yhtä klusteria.

Käynnistyssektori alkaa seuraavilla tiedoilla:

  • EB 58 90 – ehdoton hyppy ja allekirjoitus;
  • 4D 53 44 4F 53 35 2E 30 MSDOS5.0;
  • 00 02 – sektorin tavujen määrä (yleensä 512);
  • 1 tavu – sektorien lukumäärä klusterissa;
  • 2 tavua – varasektorien määrä.

Lisäksi käynnistyssektori sisältää seuraavat tärkeät tiedot:

  • 0x10 (1 tavu) – FAT-taulukoiden määrä (yleensä 2);
  • 0x20 (4 tavua) – levyn sektoreiden määrä;
  • 0x2С (4 tavua) – juurihakemiston klusterin numero;
  • 0x47 (11 tavua) – levyn tunniste;
  • 0x1FE (2 tavua) – käynnistyssektorin allekirjoitus (55 AA).

Tiedostojärjestelmän tietosektori sisältää:

  • 0x00 (4 tavua) – allekirjoitus (52 52 61 41);
  • 0x1E4 (4 tavua) – allekirjoitus (72 72 41 61);
  • 0x1E8 (4 tavua) – vapaiden klustereiden määrä, -1, jos tuntematon;
  • 0x1EC (4 tavua) – viimeksi tallennetun klusterin numero;
  • 0x1FE (2 tavua) – allekirjoitus (55 AA).

FAT-taulukko sisältää tietoja levyn kunkin klusterin tilasta. FAT-taulukon alemmat 2 tavua tallentavat F8 FF FF 0F FF FF FF FF (joka vastaa fyysisesti poissa olevien klustereiden 0 ja 1 tilaa). Seuraavaksi kunkin klusterin tila sisältää sen klusterin numeron, jossa nykyinen tiedosto jatkuu, tai seuraavat tiedot:

  • 00 00 00 00 – klusteri on ilmainen;
  • FF FF FF 0F – nykyisen tiedoston loppu.
  • 8 tavua – tiedoston nimi;
  • 3 tavua – tiedostopääte;

Juurihakemisto sisältää joukon 32-bittisiä tietueita jokaisesta tiedostosta, jotka sisältävät seuraavat tiedot:

Kun työskentelet pitkien tiedostonimien kanssa (mukaan lukien venäläiset nimet), tiedostonimi koodataan UTF-16-koodausjärjestelmällä. Tässä tapauksessa kunkin merkin koodaamiseen varataan 2 tavua. Tässä tapauksessa tiedoston nimi kirjoitetaan seuraavassa rakenteessa:

  • 1 sekvenssitavu;
  • 10 tavua sisältävät tiedostonimen 5 alinta merkkiä;
  • 1 tavun attribuutti;
  • 1 tavu varattu;
  • 1 tavu – DOS-nimen tarkistussumma;
  • 12 tavua sisältävät tiedostonimen 3 alinta merkkiä;
  • 2 tavua – ensimmäisen klusterin numero;
  • pitkän nimen muut merkit.

Työskentely tiedostojen kanssa C-kielellä

Ohjelmoijalle avoin tiedosto esitetään luku- tai kirjoitusjaksona. Kun tiedosto avataan, se yhdistetään I/O-virta. Lähtötiedot kirjoitetaan virtaan, tulotiedot luetaan virrasta.

Kun virta avataan I/O:lle, se liitetään standardinmukaiseen TIEDOSTOrakenteeseen, joka on määritelty tiedostossa stdio.h. FILE-rakenne sisältää tarvittavat tiedot tiedostosta.

Tiedoston avaaminen tapahtuu fopen()-funktiolla, joka palauttaa osoittimen TIEDOSTOrakenteeseen, jota voidaan käyttää tiedoston myöhemmissä toimissa.

FILE *fopen(nimi, tyyppi);


nimi – avattavan tiedoston nimi (mukaan lukien polku),
tyyppi on osoitin merkkijonoon, joka määrittää, kuinka tiedostoa käytetään:
  • "r" - avaa tiedosto lukemista varten (tiedoston on oltava olemassa);
  • "w" - avaa tyhjä tiedosto kirjoittamista varten; jos tiedosto on olemassa, sen sisältö menetetään;
  • "a" - avaa tiedosto loppuun kirjoittamista varten (lisäämistä varten); tiedosto luodaan, jos sitä ei ole olemassa;
  • "r+" - avaa tiedosto lukemista ja kirjoittamista varten (tiedoston on oltava olemassa);
  • "w+" - avaa tyhjä tiedosto lukemista ja kirjoittamista varten; jos tiedosto on olemassa, sen sisältö menetetään;
  • "a+" - avaa tiedosto lukemista ja liittämistä varten, jos tiedostoa ei ole, se luodaan.

Palautusarvo on osoitin avoimeen streamiin. Jos virhe havaitaan, palautetaan NULL.

Fclose()-funktio sulkee virran tai virrat, jotka liittyvät fopen()-funktiolla avattuihin tiedostoihin. Suljettavan virran määrää fclose()-funktion argumentti.

Palautusarvo: arvo 0, jos stream suljettiin onnistuneesti; vakio EOF, jos tapahtui virhe.

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

#sisältää
int main() (
FILE *fp;
char name = "oma.txt" ;
if ((fp = fopen(nimi, "r" )) == NULL )
{
printf( "Tiedoston avaaminen epäonnistui");
getchar();
paluu 0;
}
// onnistui avaamaan tiedoston
... // vaadittavat toimenpiteet datalle
fclose(fp);
getchar();
paluu 0;
}

Hahmon lukeminen tiedostosta:

char fgetc(stream);


Funktioargumentti on osoitin FILE-tyypin virtaan. Funktio palauttaa luetun merkin koodin. Jos tiedoston loppu saavutetaan tai tapahtuu virhe, palautetaan vakio EOF.

Symbolin kirjoittaminen tiedostoon:

fputc(char, stream);

Funktion argumentit ovat merkki ja osoitin FILE-tyypin virtaan. Funktio palauttaa luetun merkin koodin.

Funktiot fscanf() ja fprintf() ovat samanlaisia ​​kuin scanf()- ja printf()-funktiot, mutta ne toimivat datatiedostojen kanssa, ja niiden ensimmäinen argumentti on tiedostoosoitin.

fscanf(stream, "InputFormat", argumentit);

Tunnisteet: Tekstitiedostot, fopen, fclose, feof, setbuf, setvbuf, fflush, fgetc, fprintf, fscanf, fgets, puskuroitu virta, puskuroimaton stream.

Työskentely tekstitiedostojen kanssa

Tekstitiedoston kanssa työskentely on samanlaista kuin konsolin kanssa: muotoiltuja syöttötoimintoja käyttämällä tallennamme tiedot tiedostoon, muotoiltujen tulostefunktioiden avulla luemme dataa tiedostosta. On monia vivahteita, joita tarkastelemme myöhemmin. Tärkeimmät tehtävät ovat

  • 1. Avaa tiedosto, jotta sitä voi käyttää. Näin ollen voit avata sen lukemista, kirjoittamista, lukemista ja kirjoittamista, uudelleenkirjoittamista tai tiedoston loppuun kirjoittamista varten jne. Kun avaat tiedoston, voi myös tapahtua joukko virheitä - tiedostoa ei ehkä ole olemassa, se voi olla väärä tiedostotyyppi, sinulla ei ehkä ole oikeutta käsitellä tiedostoa jne. Kaikki tämä on otettava huomioon.
  • 2. Työskentely suoraan tiedoston kanssa - kirjoittaminen ja lukeminen. Tässä on myös muistettava, että emme työskentele hajasaantimuistilla, vaan puskuroidulla streamilla, joka lisää omat erityispiirteensä.
  • 3. Sulje tiedosto. Koska tiedosto on ohjelman ulkopuolinen resurssi, jos sitä ei suljeta, se roikkuu edelleen muistissa, mahdollisesti myös ohjelman sulkemisen jälkeen (esimerkiksi avointa tiedostoa ei voi poistaa tai tehdä muutoksia, jne.). Lisäksi joskus on välttämätöntä olla sulkematta, vaan "avata" tiedosto uudelleen esimerkiksi käyttötilan vaihtamiseksi.

Lisäksi on useita tehtäviä, joissa meidän ei tarvitse käyttää tiedoston sisältöä: uudelleennimeäminen, siirtäminen, kopioiminen jne. Valitettavasti C-standardi ei sisällä toimintojen kuvausta näihin tarpeisiin. Ne ovat tietysti saatavilla jokaiselle kääntäjätoteutukselle. Hakemiston (kansion, hakemiston) sisällön lukeminen on myös tiedostoon pääsyä, koska kansio itsessään on tiedosto, jossa on metatietoja.

Joskus on tarpeen suorittaa joitain aputoimintoja: siirtyä haluttuun paikkaan tiedostossa, muistaa nykyinen sijainti, määrittää tiedoston pituus jne.

Tarvitset FILE-objektin työskennelläksesi tiedoston kanssa. Tämä objekti tallentaa tiedostovirran tunnisteen ja sen hallintaan tarvittavat tiedot, mukaan lukien osoittimen sen puskuriin, tiedoston sijainnin ilmaisimen ja tilailmaisimet.

FILE-objekti on itsessään rakenne, mutta sen kenttiä ei pitäisi käyttää. Kannettavan ohjelman on käsiteltävä tiedostoa abstraktina objektina, joka mahdollistaa pääsyn tiedostovirtaan.

Muistin luominen ja varaaminen FILE-tyypin objektille suoritetaan fopen- tai tmpfile-funktiolla (on muitakin, mutta keskitymme vain näihin).

Fopen-toiminto avaa tiedoston. Se vastaanottaa kaksi argumenttia - merkkijonon tiedoston osoitteella ja merkkijonon tiedoston käyttötilalla. Tiedostonimi voi olla joko absoluuttinen tai suhteellinen. fopen palauttaa osoittimen FILE-objektiin, jota voidaan käyttää tiedoston lisäämiseen.

TIEDOSTO* fopen(const char* tiedostonimi, const char* tila);

Avataan esimerkiksi tiedosto ja kirjoitetaan siihen Hello World

#sisältää #sisältää #sisältää void main() ( //Tiedostomuuttujan avulla pääsemme tiedostoon FILE *file; //Avaa tekstitiedosto kirjoitusoikeuksilla file = fopen("C:/c/test.txt", "w+t") ; //Kirjoita tiedostoon fprintf(file, "Hei, maailma!" //Sulje tiedosto fclose(file);

Itse fopen-toiminto varaa muistin kohteelle puhdistuksen suorittaa fclose-toiminto. Tiedosto on suljettava, se ei sulkeudu itsestään.

Fopen-toiminto voi avata tiedoston teksti- tai binääritilassa. Oletusarvo on teksti. Pääsytila ​​voi olla seuraava

Tiedostojen käyttövaihtoehdot.
Tyyppi Kuvaus
r Lukeminen. Tiedoston on oltava olemassa.
w Kirjoita uusi tiedosto. Jos samanniminen tiedosto on jo olemassa, sen sisältö menetetään.
a Kirjoita tiedoston loppuun. Paikannustoiminnot (fseek, fsetpos, frewind) jätetään huomiotta. Tiedosto luodaan, jos sitä ei ole olemassa.
r+ Lukeminen ja päivitys. Osaat sekä lukea että kirjoittaa. Tiedoston on oltava olemassa.
w+ Tallennus ja päivitys. Uusi tiedosto luodaan. Jos samanniminen tiedosto on jo olemassa, sen sisältö menetetään. Osaat sekä kirjoittaa että lukea.
a+ Lopeta viesti ja päivitä. Paikannustoiminnot ovat vain luku -tilassa, eikä niitä kirjoiteta. Jos tiedostoa ei ollut olemassa, luodaan uusi.

Jos tiedosto on avattava binääritilassa, lisätään rivin loppuun kirjain b, esimerkiksi "rb", "wb", "ab" tai sekatilassa "ab+", " wb+, "ab+". B:n sijasta voit lisätä kirjaimen t, jolloin tiedosto avautuu tekstitilassa. Se riippuu toteutuksesta. Uudessa C-standardissa (2011) x-kirjain tarkoittaa, että fopenin pitäisi epäonnistua, jos tiedosto on jo olemassa. Täydennetään vanhaa ohjelmaamme: avataan tiedosto uudelleen ja mietitään mitä sinne kirjoitimme.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tiedosto; char-puskuri; tiedosto = fopen("C:/c/test.txt", "w"); fprintf(tiedosto, "Hei, maailma!"); fclose(tiedosto); tiedosto = fopen("C:/c/test.txt", "r"; fgets(puskuri, 127, tiedosto);

Fgets-funktion sijasta voit käyttää fscanfia, mutta sinun on muistettava, että se pystyy lukemaan vain rivin ensimmäiseen välilyöntiin asti.
fscanf(tiedosto, "%127s", puskuri);

Tiedoston avaamisen ja sulkemisen sijasta voit myös käyttää freopen-toimintoa, joka "avaa" tiedoston uudelleen uusilla käyttöoikeuksilla.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tiedosto; merkkipuskuri; tiedosto = fopen("C:/c/test.txt", "w"); fprintf(tiedosto, "Hei, maailma!"); freopen("C:/ c/test.txt", "r", tiedosto); fgets(puskuri, 127, tiedosto); printf("%s", puskuri); fclose(tiedosto); getch(); )

Funktiot fprintf ja fscanf eroavat printf:stä ja scanf:stä vain siinä, että ne ottavat ensimmäisenä argumenttinaan osoittimen TIEDOSTOON, johon ne tulostavat tai josta ne lukevat tietoja. Kannattaa heti lisätä, että printf- ja scanf-toiminnot voidaan helposti korvata fprintf- ja fscanf-funktioilla. Käyttöjärjestelmässä (tarkastelemme yleisimpiä ja sopivimpia käyttöjärjestelmiä) on kolme standardivirtaa: standardilähtövirta stdout, standarditulovirta stdin ja standardivirhelähtövirta stderr. Ne avautuvat automaattisesti, kun sovellus käynnistetään, ja ne liittyvät konsoliin. Esimerkki

#sisältää #sisältää #sisältää void main() ( int a, b; fprintf(stdout, "Anna kaksi numeroa\n"); fscanf(stdin, "%d", &a); fscanf(stdin, "%d", &b); if (b == 0) ( fprintf(stderr, "Virhe: jaa nolla"); ) else ( fprintf(stdout, "%.3f", (float) a / (float) b); ) getch();

Virhe avattaessa tiedostoa

Jos fopen-funktiokutsu epäonnistuu, se palauttaa NULL-arvon. Virheitä työskennellessään tiedostojen kanssa tapahtuu melko usein, joten aina kun avaamme tiedoston, meidän on tarkistettava työn tulos

#sisältää #sisältää #sisältää #define ERROR_OPEN_FILE -3 void main() ( TIEDOSTO *tiedosto; merkkipuskuri; tiedosto = fopen("C:/c/test.txt", "w"); if (tiedosto == NULL) ( printf("Virhe avattaessa tiedosto"); getch(); exit(ERROR_OPEN_FILE); ) fprintf(tiedosto, "Hei, maailma!"); freopen("C:/c/test.txt", "r", tiedosto); if (tiedosto = = NULL) ( printf("Virhe avattaessa tiedostoa"); getch(); exit(ERROR_OPEN_FILE); ) fgets(puskuri, 127, tiedosto); printf("%s", puskuri(tiedosto); ; )

Ongelma syntyy, kun useita tiedostoja avataan kerralla: jos yhtä niistä ei voida avata, loput on myös suljettava

FILE *inputFile, *outputFile; etumerkitön m, n; allekirjoittamaton i, j; syöteTiedosto = fopen(TUOTETIEDOSTO, VAIN LUKU); if (inputFile == NULL) ( printf("Virhe avattaessa tiedostoa %s", INPUT_FILE); getch(); exit(3); ) outputFile = fopen(OUTPUT_FILE, WRITE_ONLY); if (outputFile == NULL) ( printf("Virhe avattaessa tiedostoa %s", OUTPUT_FILE); getch(); if (inputFile != NULL) ( fclose(inputFile); ) exit(4); ) ...

Yksinkertaisissa tapauksissa voit toimia suoraan, kuten edellisessä koodissa. Monimutkaisemmissa tapauksissa käytetään menetelmiä, jotka korvaavat RAII:n C++:sta: kääreet tai kääntäjän ominaisuudet (puhdistus GCC:ssä) jne.

Tietojen puskurointi

Kuten aiemmin mainittiin, kun tuotamme dataa, se sijoitetaan ensin puskuriin. Puskuri tyhjennetään

  • 1) Jos se on täynnä
  • 2) Jos virta on suljettu
  • 3) Jos nimenomaisesti osoitamme, että puskuri on tyhjennettävä (poikkeuksiakin löytyy :)).
  • 4) Myös tyhjennetään, jos ohjelma on suoritettu onnistuneesti. Samalla kaikki tiedostot suljetaan. Jos kyseessä on ajonaikainen virhe, tämä ei välttämättä tapahdu.

Voit pakottaa puskurin purkamisen kutsumalla fflush(File *) -funktiota. Katsotaanpa kahta esimerkkiä - puhdistuksella ja ilman.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tiedosto; merkki c; tiedosto = fopen("C:/c/test.txt", "w"); do ( c = getch(); fprintf(tiedosto, "%c", c fprintf(stdout, "%c", c) //fflush(tiedosto);

Peruuta huuhtelukutsu. Avaa tekstitiedosto ajon aikana ja katso toimintaa.

Voit määrittää tiedostopuskurin itse asettamalla oman koon. Tämä tehdään funktiolla

Void setbuf(FILE * stream, char * puskuri);

joka vie jo avoimen TIEDOSTON ja osoittimen uuteen puskuriin. Uuden puskurin koko ei saa olla pienempi kuin BUFSIZ (esimerkiksi nykyisessä työasemassa BUFSIZ on 512 tavua). Jos välität NULL:n puskuriksi, virta muuttuu puskuroimattomaksi. Voit myös käyttää toimintoa

Int setvbuf(TIEDOSTO * virta, char * puskuri, int tila, koko_t koko);

joka hyväksyy mielivaltaisen kokoisen puskurin. Tila voi ottaa seuraavat arvot

  • _IOFBF- täysi puskurointi. Tiedot kirjoitetaan tiedostoon, kun se on täynnä. Luettaessa puskuri katsotaan täyteen, kun syöttötoimintoa pyydetään ja puskuri on tyhjä.
  • _IOLBF- lineaarinen puskurointi. Tiedot kirjoitetaan tiedostoon, kun se on täynnä tai kun rivinvaihtomerkki havaitaan. Lukeessa puskuri täytetään rivinvaihtomerkkiin, kun syöttötoimintoa pyydetään ja puskuri on tyhjä.
  • _IONBF– ei puskurointia. Tässä tapauksessa koko- ja puskuriparametrit jätetään huomioimatta.
Jos onnistuu, funktio palauttaa arvon 0.

Esimerkki: asetetaan oma puskurimme ja katsotaan kuinka tiedostosta lukeminen tapahtuu. Anna tiedoston olla lyhyt (jotain kuten Hello, World!), ja luemme sen merkki merkiltä

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tulo ​​= NULL; merkki c; merkkipuskuri = (0); input = fopen("D:/c/text.txt", "rt"); setbuf(tulo, puskuri); while ( !feof(syöte)) ( c = fgetc(syöttö); printf("%c\n", c); printf("%s\n", puskuri); _getch(); ) fclose(syöttö)

Voidaan nähdä, että tiedot ovat jo puskurissa. Lukeminen merkki merkiltä tapahtuu puskurista.

feof

Funktio int feof(FILE * stream); palauttaa tosi, jos tiedoston loppu saavutetaan. Toimintoa on kätevä käyttää, kun joudut käymään koko tiedoston läpi alusta loppuun. Olkoon tiedosto, jonka tekstisisältö on text.txt. Luemme tiedoston merkki merkiltä ja näytämme sen näytöllä.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tulo ​​= NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Virhe avattaessa tiedostoa") _getch(0);

Kaikki olisi hyvin, mutta feof-toiminto ei toimi oikein... Tämä johtuu siitä, että "tiedoston lopun" käsitettä ei ole määritelty. Virhe, joka usein tapahtuu käytettäessä feofia, on se, että viimeinen luettu data tulostetaan kahdesti. Tämä johtuu siitä, että tiedot kirjoitetaan syöttöpuskuriin, viimeinen luku tapahtuu virheellä ja funktio palauttaa vanhan luetun arvon.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tulo ​​= NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Virhe avattaessa tiedostoa") _getch(0); ();

Tämä esimerkki epäonnistuu (todennäköisimmin) ja tulostaa tiedoston viimeisen merkin kahdesti.

Ratkaisu ei ole käyttää feofia. Tallenna esimerkiksi tietueiden kokonaismäärä tai käytä sitä tosiasiaa, että fscanf jne.-funktiot yleensä palauttavat oikein luettujen ja täsmäytettyjen arvojen määrän.

#sisältää #sisältää #sisältää void main() ( TIEDOSTO *tulo ​​= NULL; char c; input = fopen("D:/c/text.txt", "rt"); if (input == NULL) ( printf("Virhe avattaessa tiedostoa") _getch(0);

Esimerkkejä

1. Yksi tiedosto sisältää kaksi numeroa - taulukon mitat. Täytetään toinen tiedosto satunnaislukujen joukolla.

#sisältää #sisältää #sisältää #sisältää //Tiedostojen nimet ja käyttöoikeudet #define INPUT_FILE "D:/c/input.txt" #define OUTPUT_FILE "D:/c/output.txt" #define READ_ONLY "r" #define WRITE_ONLY "w" //Matriisin enimmäisarvo koko #define MAX_DIMENSION 100 //Virhe avattaessa tiedostoa #define ERROR_OPEN_FILE -3 void main() ( FILE *inputFile, *outputFile; unsigned m, n; unsigned i, j; inputFile = fopen(INPUT_FILE, READ_ONLY); if ( inputFile == NULL) ( printf("Virhe avattaessa tiedostoa %s", INPUT_FILE); getch(); exit(ERROR_OPEN_FILE); ) outputFile = fopen(OUTPUT_FILE, WRITE_ONLY) (printf("Virhe avataan tiedosto %s", OUTPUT_FILE); getch(); //Jos tiedosto voidaan avata lukemista varten, se on suljettava if (inputFile != NULL) ( fclose(inputFile); ) exit(ERROR_OPEN_FILE); ) fscanf (syötetiedosto, "%ud %ud", &m, &n); if (m > MAX_DIMENSION; ) if (n > MAX_DIMENSION; ) srand(aika(NULL));< n; i++) { for (j = 0; j < m; j++) { fprintf(outputFile, "%8d ", rand()); } fprintf(outputFile, "\n"); } //Закрываем файлы fclose(inputFile); fclose(outputFile); }

2. Käyttäjä kopioi tiedoston ja valitsee ensin toimintatilan: tiedosto voidaan tulostaa joko konsoliin tai kopioida uuteen tiedostoon.

#sisältää #sisältää #sisältää #define ERROR_FILE_OPEN -3 void main() ( TIEDOSTO *alkuperä = NULL; TIEDOSTO *lähtö = NULL; char-tiedoston nimi; int mode; printf("Anna tiedostonimi: "); scanf("%1023s", tiedostonimi); origin = fopen (tiedostonimi, "r"); if (origin == NULL) ( printf("Virhe avattaessa tiedostoa %s", tiedostonimi); getch(); exit(ERROR_FILE_OPEN); ) printf("enter mode: "%); d", &mode); if (mode == 1) ( printf("Anna tiedostonimi: "); scanf("%1023s", tiedostonimi); output = fopen(tiedostonimi, "w"); if (lähtö = = NULL ) ( printf("Virhe avattaessa tiedostoa %s", tiedostonimi); getch(); fclose(origin); exit(ERROR_FILE_OPEN); ) ) else ( output = stdout; ) while (!feof(alkuperä)) ( fprintf (lähtö , "%c", fgetc(alkuperä));

3. Käyttäjä syöttää tiedot konsolista ja ne kirjoitetaan tiedostoon, kunnes esc-näppäintä painetaan. Katso ohjelma ja katso. kuinka se käyttäytyy, jos painat askelpalautinta: mitä tulostetaan tiedostoon ja mitä tulostetaan konsoliin.

#sisältää #sisältää #sisältää #define ERROR_FILE_OPEN -3 void main() ( TIEDOSTO *lähtö = NULL; merkki c; lähtö = fopen("D:/c/test_output.txt", "w+t"); if (lähtö == NULL) ( printf ("Virhe avattaessa tiedostoa" _getch(); , stdout);

4. Tiedosto sisältää kokonaislukuja. Löydä niistä maksimi. Hyödynnetään sitä, että fscanf-funktio palauttaa oikein luettujen ja täsmäävien objektien määrän. Numero 1 tulee palauttaa joka kerta.

#sisältää #sisältää #sisältää #define ERROR_FILE_OPEN -3 void main() ( TIEDOSTO *tulo ​​= NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (tulo == NULL) ( printf("Virhe avattaessa tiedostoa"); _getch(); exit(ERROR_FILE_OPEN); ) maxn = INT_MIN; hasRead = 1; while (hasRead == 1) ( hasRead = fscanf(input, "%d", &num); if (hasRead != 1) (jatka; ) if (num >

Toinen ratkaisu on lukea numeroita, kunnes saavutamme tiedoston loppuun.

#sisältää #sisältää #sisältää #sisältää #define ERROR_FILE_OPEN -3 void main() ( TIEDOSTO *tulo ​​= NULL; int num, maxn, hasRead; input = fopen("D:/c/input.txt", "r"); if (tulo == NULL) ( printf("Virhe avattaessa tiedostoa"); _getch(); exit(ERROR_FILE_OPEN); ) maxn = INT_MIN; while (!feof(input)) ( fscanf(input, "%d", &num); if (num > maxn; ) ( maxn = määrä; ) ) printf("maksimiluku = %d", maxn);

5. Tiedosto sisältää sanoja: venäjänkielinen sana, taulukko, englanninkielinen sana, useilla riveillä. Käyttäjä syöttää englanninkielisen sanan, on tarpeen tulostaa venäjän sana.

Käännöstiedosto näyttää suunnilleen tältä

aurinko aurinko
kynä kynä
kuulakärkikynä kynä
oven ovi
windows ikkuna
tuoli tuoli
nojatuoli

ja tallennettu cp866-koodauksella (OEM 866). Se on tärkeää: myös viimeinen sanapari päättyy rivinvaihtoon.

Algoritmi on seuraava: luemme rivin tiedostosta, etsimme riviltä sarkainmerkin, korvaamme sarkainmerkin nollalla, kopioimme venäjän sanan puskurista, kopioimme englanninkielisen sanan puskurista, tarkistamme tasa-arvon.

#sisältää #sisältää #sisältää #sisältää #define ERROR_FILE_OPEN -3 void main() ( TIEDOSTO *tulo ​​= NULL; merkkipuskuri; char enWord; char ruWord; char usrWord; etumerkitön indeksi; int pituus; int löytyi; input = fopen("D:/c/input.txt ", "r"); if (input == NULL) ( printf("Virhe avattaessa tiedostoa"); _getch(); exit(ERROR_FILE_OPEN); ) printf("kirjoita sana: "); fgets(usrWord, 127, stdin wasFound = 0, while (!feof(input)) ( fgets(puskuri, 511, input); pituus = strlen(puskuri); for (indeksi = 0; indeksi)< length; index++) { if (buffer == "\t") { buffer = "\0"; break; } } strcpy(ruWord, buffer); strcpy(enWord, &buffer); if (!strcmp(enWord, usrWord)) { wasFound = 1; break; } } if (wasFound) { printf("%s", ruWord); } else { printf("Word not found"); } fclose(input); _getch(); }

6. Laske tiedoston rivien määrä. Luemme tiedoston merkki merkiltä laskemalla "\n"-merkkien määrän, kunnes kohtaamme EOF-merkin. EOF on erikoismerkki, joka ilmaisee, että syöttö on valmis eikä luettavaa ole enää dataa. Funktio palauttaa negatiivisen arvon virheen sattuessa.
HUOMAA: EOF on tyyppiä int, joten sinun on käytettävä int merkkien lukemiseen. Lisäksi standardi ei määrittele EOF:n arvoa.

#define _CRT_SECURE_NO_WARNINGS #sisällytä #sisältää #sisältää int cntLines(const char *tiedostonimi) ( int rivit = 0; int mikä tahansa; //mikä tahansa on tyyppiä int, koska EOF on tyyppiä int! TIEDOSTO *f = fopen(tiedostonimi, "r"); if (f == NULL ) ( return -1; ) do ( any = fgetc(f); //printf("%c", mikä tahansa);//debug if (mikä tahansa == "\n") ( rivit++; ) ) while(mikä tahansa ! = EOF (fclose) void main() ( printf("%d\n", cntLines("C:/c/file.txt"); _getch();

Ru-Cyrl 18-opetusohjelma Sypachev S.S. 14.4.1989 [sähköposti suojattu] Stepan Sypachev opiskelijat

Eikö vieläkään ole selvää? – kirjoita kysymyksiä postilaatikkoon