Filer och structer Del 2 Agenda: Typedef Alternativ strängläsning från fil Binära data Lagra Läsa Oväntat slut på input Spara till nästa programstart
Typedef Kan användas till att sätta ett eget namn på en befintlig typ Om standardtypens syntax är krånglig För att förtydliga Används ofta med structer för att slippa skriva struct Syntax: typedef 'deklaration' 'deklaration' har formen av en deklaration men är det inte Det som skulle blivit ett variabelnamn i 'deklaration' (om det varit en deklaration och inte en typedef) blir ett nytt typnamn
Typedef, ex Om du vill döpa om int till integer typedef int integer;... integer x; // Equivalent to: int x; Structer; typedef struct CAR CAR;... CAR bil; // Equivalent to: struct CAR bil;
Alternativ strängläsning När slutar fscanf(fp, %s, buf); att scanna? - Då det första whitespace påträffas (blank, tab eller nyrad). Problem om man vill läsa in namn (för + efter)? - Kan man inte skriva fscanf(fp, %s %s, fname, ename);? Om någon har tre namn? Eller 4? Definitiv lösning: fgets(char *buf, int size, FILE *fp); Anropas char buf[100]; FILE *fp=fopen( somefile, r );... 4 fgets(buf, 100, fp);
Alternativ strängläsning Notera att fgets är säker ( size graranterar att den inte skriver utanför arrayen). fgets lägger även in radslut (dvs '\n') i strängen - om det får plats Om man t.ex. läser in namn vill man troligen inte ha kvar radslutet Vilken utskrift skulle man få med printf( %s är %d år\n, name, age); om radslutet finns kvar? Skriv kod för att sudda bort radslutet (miniövning) i en sträng! OBS! Detta handlar om stränghantering! 5
Textformatet Hittills har vi hanterat filer med läsbart innehåll textfiler. Data har sparats i filen på ett sätt som är tänkt att tolkas som text, ex: int x=8; FILE *stream=fopen( tal.txt, w ); fprintf(stream, %d, x); Bitarna i x (4 bytes) 00000000 00000000 00000000 00001000 Bitarna i tal.txt 1 byte: teckenkoden för 8, dvs 56 00111000 6
Binära data Ett alternativ är att spara data precis som de är utan konvertering: Bitarna i x (4 bytes) 00000000 00000000 00000000 00001000 Bitarna i tal.txt (lika) 00000000 00000000 00000000 00001000 7
Binär vs text Vad betyder binär resp. text? Data i en fil (eller för den delen minnet) består av en sekvens av bitar grupperade i bytes Vad är då skillnaden? Det är en fråga om hur data är avsedda att tolkas! Om heltalet 943208504 skrivs binärt till en fil och den filen öppnas i en editor så står där 8888 Allmänt: Det som är avsett att tolkas binärt kan råka utgöra till synes vettiga teckenkoder. Det som är tänkt att tolkas som text kan också utgöra vettiga tolkningar binärt. I allmänhet blir det inte samma 8
Binär filaccess i C Det finns ingen magi i vare sig filens egenskaper eller i vad filen heter Det som avgör är hur du skriver till och läser från filen Binär access i C: fwrite(void *data, int size, int n, FILE *fp); fread(void *data, int size, int n, FILE *fp); Öppna och stänga som vanligt, men fprintf och fscanf ersätts av ovanstående vid binär filaccess 9
Exempelkod Kodfragment för att läsa 20 heltal till en array av int: int array[100], n=20; FILE *stream=fopen( binfil, rb ); if (stream)... fread(array, sizeof(int), n, stream); 10
Exempelkod Kodfragment för att läsa 20 heltal till en array av int: int array[100], n=20; FILE *stream=fopen( binfil, rb ); if (stream)... fread(array, sizeof(int), n, stream); Pekare till data Storleken på ett element Antalet element 11
Övning Skriv ett program som sparar ett int-värde så att en editor visar filens innehåll som texten: 8888 12
För och nackdelar Binärt: Data kan lagras/läsas rätt av -> mindre risk för fel Speciellt kan hela arrayer av structer överföras i en operation Smidigt att uppdatera mitt i en fil Om det blir fel: jobbigt att hitta Text: fprintf/fscanf mappar oftast inte exakt till varandra Strul med flera ord och fscanf Strul med flera ord, fgets och andra data på en rad Alltid lätt att omedelbart se fel 13
Hela structer struct person { char name[20]; int age; };... struct person array[100];... fread(array, sizeof(struct person), n, fp);... fwrite(array, sizeof(struct person), n, fp); Klart! 14
Övning Skriv ett program som gör följande: Laddar in en binär fil person till en structvariabel Skriver ut det den läst på skärmen (om den hittade något). Låter användaren ändra något i variabeln Sparar tillbaks till samma binära fil. struct person { }; char name[20]; int age; 15
Filslut Filer används vanligen för att lagra innehållet i arrayer (och ofta även stora mängder data) Hur veta antalet element vid läsning? Se till att antalet element står först i filen Loopa ett element i taget tills man detekterar slutet av filen, eller Kombinera båda 16
Ange antal element: binär fwrite(&n, sizeof(int), 1, fp); fwrite(array, sizeof(struct person), n, fp);... fread(&n, sizeof(int), 1, fp); fread(array, sizeof(struct person), n, fp); 17
Sök filslut: binär fwrite(array, sizeof(struct person), n, fp);... while (!feof(fp)) { fread(&array[i], sizeof(struct person), 1, fp); if (!feof(fil)) i++; } 18
Kombinera: binär fwrite(&n, sizeof(int), 1, fp); fwrite(array, sizeof(struct person), n, fp);... fread(&n, sizeof(int), 1, fp); m=fread(array, sizeof(struct person), n, fp); if (n!=m) { n = m; // Error handling: incomplete load } 19
Textfil Analogt: Inspektera returvärdet från fscanf, fgets etc för att avgöra om filslut Alt. använd feof() till samma ändamål Precis som för binärfiler är det förstås en fördel att vid lagring av data se till att antalet element i arrayer finns först 20
Sök filslut: text Måste läsa ett array-element i taget och varje fält för sig... do { n=fscanf(fp, %d %s, &pers[i].age, &pers[i].name); if (n==2) i++; } while (n==2); På liknande sätt som binärläsning att detektera med feof() 21
Kombinera: text Måste läsa ett array-element i taget och varje fält för sig... fscanf(fp, %d, &n); i = 0; while (i<n){ } n=fscanf(fp, %d %s, &pers[i].age, &pers[i].name); if (n==2) i++; else { } /* error handling */ n = i; 22
Bättre felhantering Om antalet element lagras i filen: Filslut kan användas till att detektera fel Enklare att läsa in om binära data 23
Ändra data i fil Standard metod som rekommenderas funkar lika bra med text som binära data 1. Läs in alla data från fil till programmets variabler i början av programmet 2. Gör det som programmet ska göra (t.ex. ändra några data i programmets variabler) 3. Spara alla variablers värden till filen innan programmet avslutas 24
Övning (extra) Skapa en större binärfil (en med t.ex. 3 personposter) genom att modifiera tidigare övning. Gör sedan en funktion som kan skriva ut innehållet i filen genom att bara läsa en post i taget (ingen array deklareras). Gör ytterligare en funktion som låter användaren skriva in en ny personpost, och ange på vilken plats den ska sparas. Denna sparas genom att använda uppdateringsläge. 25
Ändra data i fil (2) Öppna filen i uppdateringsläge fopen(filename, r+b ); Ofta inte så användbar med textfiler Aktuell position i början av filen efter öppning Positionen kan ändras med t.ex. fseek(file*, long offset, int whence); Ex (om man vill ändra post 13): fseek(fp, sizeof(xxxx)*13+sizeof(int), SEEK_SET); Alternativ: fgetpos() och fsetpos() 26