Materijali/Čas 15

Čas 15 · fgets, feof, binarni režim i fseek

Fajlovi: prolazak, kopiranje i pozicioniranje

Čitamo sve podatke bez dupliranja poslednje vrednosti, kopiramo tekst red po red, zapisujemo binarne podatke i biramo tačnu poziciju u fajlu.

MAPA ČASA

Od ideje do prvog C programa

1
program
2
podaci
3
promenljiva
4
pseudokod
5
C program

Na kraju je dovoljno da:

  • Vodi petlju povratnom vrednošću fscanf ili fgets, a ne funkcijom feof.
  • Kopiraj tekstualni fajl uz proveru čitanja, upisa i oba otvaranja.
  • Razlikuj tekstualni i binarni zapis i proveri rezultate fread i fwrite.
  • Koristi fseek, ftell i rewind za kontrolu pozicije u fajlu.

01 · KORAK

Pokušaj čitanja odlučuje da li postoji sledeći podatak

Kada ne znamo koliko brojeva fajl sadrži, fscanf poziv stavljamo u uslov petlje. Za format %d rezultat 1 znači da je x upravo dobio novu celobrojnu vrednost.

Rezultat 0 znači da sledeći sadržaj ne odgovara formatu, a EOF da pre prve dodele više nema ulaza ili je nastala greška. Telo ne sme da koristi x kada nema nove dodele.

int x;
while (fscanf(f, "%d", &x) == 1) {
    printf("%d ", x);
}
Prati povratnu vrednostZa sadržaje 4 7 9 i 4 7 x 9 napiši rezultat svakog fscanf poziva i tačno mesto na kome petlja staje.
Novo čitanje ili nema obradePromenljiva x se obrađuje samo u prolazu u kome je fscanf vratio 1. Tako se stara vrednost nikada ne koristi kao da je upravo pročitana.

02 · KORAK

feof opisuje prethodni neuspeh, ne sledeće čitanje

feof(f) nije pitanje 'da li je sledeći podatak poslednji'. Indikator kraja postaje postavljen tek kada neka funkcija čitanja pokuša da pređe kraj toka.

Zato obrazac while (!feof(f)) može još jednom da obradi stari x ili stari sadržaj bafera. Posle petlje vođene čitanjem, feof i ferror mogu da pomognu da razlikujemo običan kraj od greške toka.

while (fscanf(f, "%d", &x) == 1) {
    /* x je nov i važeći */
}
if (!feof(f)) {
    /* neispravan podatak ili greška */
}
Popravi petljuU kodu sa while (!feof(f)) premesti odluku o prolasku u povratnu vrednost fscanf. Zatim odredi šta se događa kada se među brojevima pojavi slovo.
PAŽNJA - čest ispitni primer je pogrešanAko se u starom materijalu pojavi while (!feof(f)), objasni stvarno ponašanje i pokaži ispravan obrazac. feof se ne koristi za predviđanje uspeha sledećeg čitanja.

03 · KORAK

fgets čita red zajedno sa razmacima

fgets prima bafer, njegov kapacitet i otvoreni tok. U bafer stavlja najviše kapacitet minus jedan znak i završava sadržaj znakom \0.

Ako je znak novog reda pročitan i ima mesta, ostaje u baferu. Poslednji red bez znaka novog reda i dalje se vraća kao uspešno pročitani sadržaj.

char red[128];
while (fgets(red, sizeof red, f) != NULL) {
    printf("%s", red);
}
Broj redovaDopuni program koji broji redove kraće od bafera i proveri slučaj u kome poslednji red nema znak novog reda.
Nemoj dodavati drugi novi redAko red već sadrži \n, printf("%s\n", red) pravi dodatnu praznu liniju. Za prikaz bez izmene koristi printf("%s", red).

04 · KORAK

Jedno uspešno čitanje vodi jednom upisu

Za kopiranje se ulaz otvara režimom r, a izlaz režimom w. Svaki red koji fgets uspešno vrati odmah se prosleđuje funkciji fputs.

Ako otvaranje izlaza ne uspe, ulaz se zatvara. Ako fputs vrati EOF, oba toka se zatvaraju pre izlaska sa greškom.

char red[256];
while (fgets(red, sizeof red, ulaz) != NULL) {
    if (fputs(red, izlaz) == EOF) {
        /* zatvaranje i greška */
    }
}
Kopija bez dodatih znakovaNapiši ceo program za prvi.txt i drugi.txt. Proveri i poslednji red koji nema \n: kopija mora da ostane ista kao original.
Režim w menja odredište odmahStari sadržaj drugi.txt nestaje pri uspešnom otvaranju sa w, pre prvog fputs poziva.

05 · KORAK

Tekst čuva znakove, binarni fajl čuva bajtove

U tekstualnom fajlu broj 125 vidi se kao tri znaka. U binarnom fajlu fwrite prenosi bajtove memorijske reprezentacije podatka; fread mora da zna veličinu i broj elemenata.

Režimi rb i wb jasno biraju binarni tok. Povratna vrednost fread i fwrite je broj uspešno prenetih elemenata, pa se poredi sa očekivanim brojem.

if (fwrite(a, sizeof a[0], 5, f) != 5) { /* greška */ }
if (fread(b, sizeof b[0], 5, f) != 5) { /* greška */ }
Pet elemenata, ne broj bajtovaDopuni fwrite i fread za niz od pet int vrednosti i objasni šta znači rezultat 3.
Format binarnog zapisa mora biti poznatDrugi program mora da zna kojim redom i kojim tipovima su podaci upisani. Binarni fajl nije sam po sebi čitljiv kao tekst.

06 · KORAK

Pozicija može da se pomeri bez čitanja svega pre nje

fseek prima tok, pomeraj i početnu tačku SEEK_SET, SEEK_CUR ili SEEK_END. Vraća 0 kada je pomeranje uspešno. ftell vraća trenutnu poziciju, a rewind vraća tok na početak i briše indikatore kraja i greške.

U binarnom fajlu možemo otići na poslednji int pomoću negativnog sizeof(int) od kraja. Za tekstualne tokove proizvoljni bajtovski pomeraji nisu jednako prenosivi; početak se bezbedno dobija sa rewind ili fseek(f, 0, SEEK_SET).

if (fseek(f, -(long)sizeof(int), SEEK_END) != 0) return 1;
int poslednji;
if (fread(&poslednji, sizeof poslednji, 1, f) != 1) return 1;
Pročitaj samo poslednji elementZa binarni fajl sa pet int elemenata označi poziciju posle fseek i napiši proveru jednog fread poziva.
Proveri i pomeranjeNe koristi rezultat narednog fread ako fseek nije vratio 0. ftell vraća -1L kada pozicija ne može da se odredi.

ZATVARANJE

Brza provera pre domaćeg

  1. Koja je razlika između algoritma i programa?
  2. Šta je ime, a šta vrednost promenljive?
  3. Kako svojim rečima čitaš x = x + 1?
  4. Koji red prvog programa računa, a koji prikazuje rezultat?