270 likes | 376 Vues
9. přednáška 3. 4. 2008 - soubory (soubory na úrovni proudu dat (stream)) Studijní materiály najdete na adrese: http://www.uai.fme.vutbr.cz/~vdumek/. Soubory v jazyce C. - symbolické číslo souboru (file handle) - celá čísla pro elemen- tární přístup na úrovni operačního systému
E N D
9. přednáška 3. 4. 2008 - soubory (soubory na úrovni proudu dat (stream)) Studijní materiály najdete na adrese: http://www.uai.fme.vutbr.cz/~vdumek/
Soubory v jazyce C - symbolické číslo souboru (file handle) - celá čísla pro elemen- tární přístup na úrovni operačního systému - proud dat (stream) - symbolická jména souboru, zajišťuje maxi- mální portabilitu, nezávislé na technickém vybavení - pro komunikaci s periferiemi se používají stejné principy jako pro soubory S.č. Použití Zařízení Proud dat 0 Standardní vstup CON stdin 1 Standardní výstup CON stdout 2 Chybový výstup CON stderr 3 Obousměrný AUX stdaux 4 Výstup na tiskárnu PRN stdprn - stdaux a stdprn jsou specifické pro operační systém MS-DOS, nelze je používat jinde, stdin a stdout lze přesměrovat jinam
Soubory v jazyce C - stdin, stdout - lze přesměrovat na jiná zařízení, pokud nejsou přesměrovány, jsou sdruženy s klávesnicí a obrazovkou (CON - konzola), stdin má vyrovnávací paměť pro jeden řádek, stdout je bez vyrovnávací paměti - stderr - pevně sdružený s obrazovkou - stdprn - pevně sdružený s tiskárnou - stdaux - pevně sdružený s portem pomocného zařízení,závisí na konfiguraci, obvykle COM1) Mimo stdin jsou všechny proudy dat bez vyrovnávací paměti. - pracujeme s ním prostřednictvím ukazatele na strukturu FILE - objekt FILE nesmíme sami vytvářet, kopírovat, je možné pou- ze využívat funkce, určené k manipulaci s touto strukturou - FILE uchovává řadu informací o souboru: aktuální pozice, sdru- žená vyrovnávací paměť, indikátory chyby, konce souboru Soubory na úrovni proudu dat
Soubory v jazyce C typedef struct { short level; /* fill/empty level of buffer */ unsigned flags; /* File status flags */ char fd; /* File descriptor */ unsigned char hold; /* Ungetc char if no buffer */ short bsize; /* Buffer size */ unsigned char *buffer; /* Data transfer buffer */ unsigned char *curp; /* Current active pointer */ unsigned istemp; /* Temporary file indicator */ short token; /* Used for validity checking */ } FILE; /* This is the FILE object */ Textové a binární soubory - samotná struktura FILE neobsahuje informaci o režimu, v jakém byl soubor otevřen, tuto informaci musíme explicitně určit - pokud neuvedeme specifikaci transformačního režimu, otevře se soubor podle stavu globální proměnné _fmode (O_BINARY, O_TEXT, fcntl.h) - pro textové soubory se v jazyce C předpokládá, že řádky jsou odděleny LF (0x0A)
Soubory v jazyce C - v DOSu se mezi dva řádky ukládá dvojice znaků CR (0x0D) a LF. Při vstupu tedy dochází k převodu dvojice znaků CR/LF na jediný znak LF a při výstupu obráceně - u binárních souborů jsou všechny znaky rovnocené - pokud je proud dat sdružen se souborem, používá se vyrovnávací paměť, zrychlí se vstup i výstup void setbuf(FILE *dat_pr, char *blk); int setvbuf(FILE *dat_pr, char *blk, int typ, unsigned velkst); - prototypy jsou v STDIO.H, VP můžeme definovat vlastní, změ- nit velikost používané, zcela ji vyřadit - pokud je ve fci setbuf směrník na VP NULL, není paměť vy- užívána, pokud ano, její velikost je dána dle BUFSIZE (512) Vyrovnávací paměť
Soubory v jazyce C - setvbuf, pokud je směrník NULL, alokuje se VP pomocí malloc, velikost je 0-32767 (unsigned) - parametr typ: _IOFBF (0) - plně využitá VP _IOLBF (1) - jeden řádek _IONBF (2) - bez VP - nastavení VP se může použít pouze bezprostředně po otevření souboru nebo po přestavení ukazatele (fopen, fseek), v jiných případech je volání fce destruktivní - pozor na lokální vyrovnávací paměť a opuštění fce před uvolně- ním (uzavřením souboru, ukončení programu) - VP se automaticky vyprazdňuje po zaplnění, uzavření souboru, ukončení programu, fseek, použitím fcí int fflush(FILE *dat_pr); (vrací 0 nebo -1) int fflushall(void); (vrací počet otevřených proudů) - prototypy v STDIO.H
Soubory v jazyce C - pokud s proudem dat provádíme zápis i čtení, nemůžeme oba způsoby libovolně kombinovat, je třeba mezi nimi vyprázdnit VP int main (void) { FILE *input, *output; char buff[512]; input=fopen(“file.in”, “r+b”); output=fopen(“file.out”, “r+b”); if (setvbuf(input, buff, _IOFBF, 512) != 0) /* vlastni buffer */ printf(“spatne nastaveni vstupni pameti”); else printf(“vstupni pamet nastavena”); if (setvbuf(output, NULL, _IOLBF, 132) != 0) /* jeden radek */ printf(“spatne nastaveni vystupni pameti”); else printf(“vystupni pamet nastavena”); fclose(input); fclose(output); }
Soubory v jazyce C Otevření proudu dat - získání ukazatele na strukturu FILE, používat výhradně standar- dní funkce, není potřeba volat při použití předdefinovaných proudů FILE* fopen(const char *jmeno_souboru, const char *typ_souboru); Typ souboru:- r otevření pouze pro čtení - w otevření pouze pro zápis - a otevření pro doplnění - r+ otevření existujícího pro aktualizaci - w+ dtto - a+ otevření pro aktualizaci na konci souboru #include <stdio.h> FILE *muj_soubor; muj_soubor=fopen(“muj_soubor.txt”,”w”); fprintf(stdprn, “\n%s”, “Vystup na tiskarnu”); /* nemusi se otevirat */ fprintf(muj_soubor, “\n%s”, “Vystup do meho souboru”);/* musi se otevirat */
Soubory v jazyce C - vedle otevření souboru se může použít i funkce ke změně sdru- ženého souboru s proudem dat FILE* freopen(const char *jm_souboru, const char *typ, FILE *dat_pr); - jako výhodné se ukazuje vytvoření vlastní funkce k otevření souboru void main(void) FILE *otevri1(char *ret) { { FILE *soub; FILE *pom; if(soub=otevri1(“tmp1.txt”) != NULL) pom=fopen(ret, “w”); /* soubor je uspesne otevren */ return pom; else } /* soubor se nepodarilo otevrit */ - pro uzavření proudu dat jsou k dispozici dvě funkce, jedna je platná pro všechny otevřené soubory, druhá pouze pro jeden soubor, obě pracují za stejných podmínek
Soubory v jazyce C #include <stdio.h> /* program vytvori zalohu souboru AUTOEXEC.BAT */ int main(void) { FILE *in, *out; if((in=fopen(“\\AUTOEXEC.BAT”,”rt”)==NULL) { fprintf(stderr, “nemohu otevrit vstupni soubor\n”); return -1; } if((out=fopen(“\\AUTOEXEC.BAK”,”wt”)==NULL) { fprintf(stderr, “nemohu vytvorit vystupni soubor\n”); return -1; } while(!feof(in)) fputc(fgetc(in), out); fclose(in); fclose(out); return 0; }
Soubory v jazyce C #include <string.h> #include <stdio.h> int main(void) { FILE *fp; char buf[11] =“0123456789”; /* vytvor soubor obsahujici 10 byte */ fp=fopen(“DUMMY.FIL”, “w”); fwrite(&buf[0], strlen(buf), 1, fp); /* zavri soubor */ fclose(fp); return 0; } - všechny funkce a makra, které slouží pro vstup z proudu dat, mají svůj prototyp v STDIO.H, CONIO.H. Návratové hodnoty bývají přečtené znaky v případě úspěchu, v případě neúspěchu nejsou návratové hodnoty definovány. Vstup z proudu dat
Soubory v jazyce C - funkce pro návrat znaku do proudu dat zvládne pouze jeden znak, nemůže být volána dvakrát za sebou, bezprostředně za ní musí následovat vstup z proudu dat int getc(FILE *dat_pr); makro, vrací následující znak z proudu, při chybě nebo konci souboru vrací -1 (EOF) int getch(void); funkce, čte jeden znak z klávesnice, bez echa, ná- vratová hodnota je přečtený znak, při chybě není int getchar(void); makro, getc(stdin) int getche(void); funkce, echo znaku se provede do aktuálního okna, při chybě není návratová hodnota int getw(FILE *dat_pr); funkce, vrací z proudu další celé číslo, při chybě nebo konci souboru vrací -1 (EOF), jelikož je tato hod- nota přípustná, musí se konec a chyba zjišťovat voláním feof(), ferror() int fgetc(FILE *dat_pr); funkce stejná jako makro getc() int fgetchar(void); funkce odpovídající fgetc(stdin)
Soubory v jazyce C int ungetc(char zn, FILE *dat_pr); funkce, uloží znak zn zpět do vstupního proudu, tento znak bude přečten další funkcí, lze takto vracet pouze jeden znak, volání fseek() má za následek ztrátu vraceného znaku, návratová hodnota je zpětně uložený znak int ungetch(int zn); funkce, uloží znak zpět do proudu klávesnice #include <stdio.h> int main(void) { int i=0; char ch; puts(“zadej cele cislo nasledovane znakem: “); while((ch=getchar()) != EOF && isdigit(ch)) i=10*i + ch-48; if(ch != EOF) /* jestlize to nebylo cislo, vrat ho */ ungetc(ch, stdin); printf(“i=%d, dalsi znak v bufferu je %c\n”, i, getchar()); return 0; }
Soubory v jazyce C Vstup řetězce z proudu dat - v případě funkcí, které pracují s řetězci, bývá návratová hodnota ukazatel na přečtený řetězec, nebo NULL. Čtení probíhá až do znaku nová řádka. char *gets(char *retz); funkce, přečte řetězec z stdin na adresu retz, znak nová řádka se zkonvertuje na \0, při chybě nebo konci souboru se vrací NULL, jinak ukazatel na retz char *cgets(char *retz); funkce, přečte řetězec znaků z klávesnice a uloží ho včetně jeho délky na adresu retz, funkce čte do znaku nový řádek nebo do zadaného počtu znaků, znak nová řádka se nahradí \0, retz[0] maximální počet, retz[1]- přečtený počet, vlastní znaky začínají na &retz[2], délka musí být retz[0]+3, fce vrací ukazatel na retz[2] char *fgets(char *retz, int n, FILE*dat_pr); funkce, čte znaky z proudu do řetězce retz, čtení se ukončí po n-1 znacích nebo nové řádce, znak nové řádky se nemění, ale ukládá, za ním je \0
Soubory v jazyce C #include <stdio.h> #include <conio.h> void main(void) { char blok[23], *uk; blok[0]=20; /* maximalni pocet znaku pro vstup */ uk=cgets(blok); printf(“\ncgets precetl %d znaku: \”%s\”\n, blok[1], uk); printf(“\nvraceny ukazatel je %p, blok[2] je nastaven na %p\n”, uk, &blok[2]); blok[0]=5; /* maximalni pocet znaku zmensime na 5 */ uk=cgets(blok); printf(“\ncgets precetl %d znaku: \”%s\”\n, blok[1], uk); printf(“\nvraceny ukazatel je %p, blok[2] je nastaven na %p\n”, uk, &blok[2]); } - při dosažení maximálního počtu znaků je jediným možným znakem nová řádka, ostatní znaky jsou ignorovány
Soubory v jazyce C #include <string.h> #include <stdio.h> int main(void) { FILE *stream; char string[] = “To je test!”; char msg[20]; /* otevreni souboru pro aktualizaci */ stream = fopen(“DUMMY.FIL”, “w+”); /* napis string do souboru */ fwrite(string, strlen(string), 1, stream); /* nastav na zacatek */ fseek(stream, 0, SEEK_SET); #include <stdio.h> /* cti retezec ze souboru */ fgets(msg, strlen(string)+1, stream); int main(void) /* zobraz string */ { printf(“%s”, msg); char string[80]; return 0; printf(“vstup retezce: “); } gets(string); printf(“retezec na vstupu: %s”,string); return 0; }
Soubory v jazyce C Formátovaný vstup z proudu dat - tento vstup zajišťuje skupina funkcí ...scanf(), fce této skupiny snímají vstupní pole znak po znaku a konvertují je podle daného formátu. Formátovaný vstup ukládají na adresy uváděné jako argumenty za formátovacím řetězcem. Návratovou hodnotou u všech funkcí je počet vstupních polí, které se podařilo sejmout, zkonvertovat a uložit - pokud funkce narazí na konec proudu dat, vrací EOF int scanf(const char *form[, arg, ...]); funkce, čte ze stdin a uk- ládá na místa argumentů (adresy) int cscanf(const char *form[, arg, ...]); funkce, čte data přímo z klávesnice int fscanf(FILE *dat_pr, const char *form[, arg, ...]); funkce, čte data z proudu dat
Soubory v jazyce C int sscanf(const char *retz, const char *form[, arg, ...]); funkce, data se čtou ze vstupního řetězce na místo dané adresními argu- menty, zdrojový řetězec se nijak nemění #include <stdlib.h> #include <stdio.h> int main(void) { int i; printf(“vstup celeho cisla: ”); /* cte cislo ze stdin */ if(fscanf(stdin, “%d”, &i)) printf(“cislo bylo: %i\n”, i); else { fprintf(stderr, “chyba cteni ze stdin\n”); exit -1; } return 0; }
Soubory v jazyce C Formátovací řetězce - řídí postup při snímání, konverzi a ukládání vstupních polí - počet adresních argumentů nesmí být menší, než je požadováno formátem, důsledek je fatální, přebytečné argumenty jsou igno- rovány % [*] [šířka] [velkst] druh - formátovací specifikace začíná znakem %, bez mezer následují nepovinný znak potlačení přiřazení vstupu (*) - pole se sejme, ale nepřiřadí se vstupnímu argumentu - další význam znaku * - specifikátor šířky[šířka] - nepovinný, dekadické číslo n, které určuje maximální počet znaků u přečteného vstupu - znak druhu konverze[velkst] - nepovinný modifikátor typu argumentu, určuje použitou verzi typu - short (h), long (l) - druh konverze je dán následující tabulkou
Konverzní znaky pro numerické hodnoty ZNAK VSTUP TYP ARGUMENTU d dekadické celé číslo ukazatel na int D dekadické celé číslo ukazatel na long o oktalové celé číslo ukazatel na int O oktalové celé číslo ukazatel na long x hexadecimální číslo ukazatel na int X hexadecimální číslo ukazatel na long i dek., okt. nebo hex. číslo ukazatel na int I dek., okt. nebo hex. číslo ukazatel na long u dekadické číslo bez znaménka ukazatel na unsigned U dekadické číslo bez znaménka ukazatel na unsigned long e pohyblivá čárka ukazatel na float E pohyblivá čárka ukazatel na float f pohyblivá čárka ukazatel na float F pohyblivá čárka ukazatel na float
Konverzní znaky pro znakové typy • ZNAK VSTUP TYP ARGUMENTU • c znak ukazatel na char, je-li současně dána šířka pole • S (např. %5c), pak ukazatel na pole S znaků • tj. char arg[5] • s řetězec znaků ukazatel na pole znaků char arg[] • n (žádný) ukazatel na int, počet do této chvíle přečtených • znaků se uloží do tohoto ukazatele • p hexadecimální • číslo tvaru • YYYY:ZZZZ nebo • ZZZZ ukazatel na objekt (far*, near*), konverze se • automaticky řídí použitým paměťovým modelem • % znak procenta žádná konverze se neprovádí, uloží se znak % - za vstupní pole jsou považovány všechny vstupní znaky až do nejbližšího prázdného znaku nebo do znaku, který nelze konver- tovat podle specifikace (G hexa, 8 oktalově, …), n znaků, pokud je určena šířka
Soubory v jazyce C - akceptované znaky lze určit i výčtem, uvádí se do [], lze i ne- govat %[abcd] budou se snímat všechny znaky ze vstupního pole, dokud se nenarazí na jiný znak než a,b,c,d %[^abcd] budou se snímat znaky, dokud se nenarazí na a,b,c,d - u konverze e, E, f, F (float) číslo musí být ve tvaru: [+|-]ddddddd[.]dddd[E|e][+|-]ddd ddd dekadické, oktalové nebo hexadecimální číslice - funkce scanf ukončí snímání vstupního pole a přejde k dalšímu: ve formátovacím řetězci se objevila * (sejme se, neuloží) bylo přečteno tolik znaků, kolik udává specifikátor šířky další znak nelze zkonvertovat podle aktuálního formátu další znak nepatří do vyhledávací množiny - úplné ukončení scanf: další znak je EOF, celý formátovací řetězec je vyčerpán, další znak nesouhlasí s příslušným neprázdným znakem
Výstup do proudu dat - skupina funkcí, která formátuje výstup podle formátovacího řetězce, je aplikován proměnný počet argumentů, u některých funkcí je směrování implicitně do stdout (printf), na obrazovku (cprintf), do proudu (fprintf) a do řetězce (sprintf) int printf(const char *form[, arg, ...]); funkce, do stdout int cprintf(const char *form[, arg, ...]); funkce, do aktuálního textového okna int fprintf(FILE *dat_pr, const char *form[, arg, ...]); funkce, do proudu dat int sprintf(char *retz, const char *form[, arg, ...]); funkce, do řetězce, který zakončí \0, odpovídající místo pro řetězec musí zajistit programátor vhodnou alokací - počet argumentů nesmí být menší, než ve formátovacím řetězci, přebytečné argumenty se ignorují %[příznaky][šířka][.přesn][velkst]druh
Přehled příznaků PŘÍZNAK VÝZNAM PŘÍZNAKU - zarovnání výstupu zleva, zprava doplňuje mezerami, není-li uveden, výstup se zarovnává zprava a zleva se doplní nulami nebo mezerami + numerický výsledek konverze začíná vždy znaménkem plus nebo mínus mezera je-li hodnota nezáporná, začíná výstup mezerou místo plus, záporné hodnoty začínají vždy mínus - specifikátor šířky určuje minimální šířku pole pro výstup Použití specifikátoru šířky SPEC. ŠÍŘKY VÝSLEDNÁ ŠÍŘKA POLE n vytiskne se alespoň n znaků, má-li výsledná hodnota méně než n znaků, výsledek se doplní mezerami zleva nebo zprava, podle příznaku (-) 0n vytiskne se alespoň n znaků, má-li výsledná hodnota méně než n znaků, doplní se zleva nulami
- specifikátor přesnosti začíná vždy znakem tečka, což ji oddě- luje od specifikace šířky • Použití specifikátoru přesnosti • SPEC.PŘESNOSTI PŘESNOST VÝSTUPU • (žádný) přesnost je dána implicitně: • = 1 pro d, i, o, u, x, X • = 6 pro e, E, f • na typ c nemá vliv • pro typ s se tiskne do prvního nulového znaku • .0 pro konverze typu d, i, o, u, x, X se přesnost nastavuje • standardně, pro typ e, E, f se netiskne desetinná tečka • .n tiskne se n znaků nebo n desetinných míst, má-li výstupní • hodnota více jak n znaků, může se výsledek zarovnat nebo • zaokrouhlit (podle typu konverze) - modifikátory velikosti výstupu určují interpretaci velikosti konvertovaného argumentu F - vzdálený (far) ukazatel N - blízký (near) ukazatel h - short int, l - long int
- F - argument se interpretuje jako vzdálený ukazatel N - argument se interpretuje jako blízký ukazatel, nesmí se pou- žít u huge modelu h - u konverzí d, i, o, u, o, x, X se argument interpretuje jako short int l - u konverzí d, i, o, u, o, x, X se argument interpretuje jako long int, při konverzích g, G, f, e, E jako double • Konverze znakových hodnot • ZNAK ARGUMENT FORMÁT VÝSTUPU • c char jediný znak • s *char • char[] tiskne znaky, dokud nenarazí na koncový znak • nebo nedosáhne maximální délky (“přesnosti”) • % žádný vytiskne se znak %
Konverze numerických hodnot • ZNAK ARGUMENT FORMÁT VÝSTUPU • d int dekadické celé číslo se znaménkem • i int dekadické celé číslo se znaménkem • o int oktalové celé číslo bez znaménka • x int hexadecimální číslo bez znaménka (a,b,c,d,e,f) • X int hexadecimální číslo bez znaménka (A,B,C,D,E,F) • f float hodnota se znaménkem ve tvaru [-] dddd.dddd • e float hodnota se znaménkem ve tvaru [-] d.ddd e [+|-] ddd • E float jako e, ale v exponentu je E • g float hodnota se znaménkem ve tvaru f nebo e dle • hodnoty a přesnosti, koncové nuly a desetinná • tečka se tisknou jen je-li to nutné • G float jako g, ale s E v exponentu, byl-li použit formát e Konverze ukazatelů ZNAK ARGUMENT FORMÁT VÝSTUPU n *int uloží na místo na které odkazuje odpovídající argument, počet doposud vypsaných znaků p ukazatel vytiskne argument jako ukazatel, vzdálený ukazatel se tiskne jako XXXX:YYYY, blízké ukazatelese tisknou jako YYYY (jen offset)