Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'Input/Output og Filer
44.  Filer i C

Vi vender nu blikket mod filer i C. Vi ser på åbning og lukning af filer i C, strukturen FILE som repræsenterer filer i C, tegnvis skrivning og læsning af filer, standard input, output og error, opening modes, og endelig et lille repertoire af funktioner fra standard biblioteket til håndtering af både sekventielle og random access filer i C.

44.1 Sekventielle filer i C44.5 Standard input, output og error
44.2 Filbuffering44.6 Fil opening modes
44.3 Strukturen FILE44.7 Funktioner på sekventielle filer
44.4 Simpel skrivning og læsning af en fil44.8 Funktioner på random access filer
 

44.1.  Sekventielle filer i C
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Filer i C håndteres via biblioteket stdio.h

Sekventielle filer i C kan ses som abstraktioner over filerne i operativsystemet

Sådanne abstraktioner omtales ofte som streams

  • Åbning af en fil

    • Knytter forbindelsen til en fil i operativsystemets filhierarki

    • Angivelse af opening mode

    • Etablerer en pointer til en FILE struct

  • Processering af filen

    • stdio.h tilbyder et antal forskellige funktioner til sekventiel processering af en fil

  • Lukning af filen

    • Tømmer buffere og frigiver interne ressourcer

    • Bryder forbindelsen til filen i operativsystemet

 

44.2.  Filbuffering
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

En filbuffer er et lagerområde mellem anvendelsesprogrammet og det fysiske lager, hvor filen er gemt. Buffer ideen er illustreret i figur 44.1.

Hvis man fra en harddisk beder om ganske få bytes af data i en sekventiel læsning af en fil vil dette mest sandsynligt gå meget langsomt, med mindre man indfører en buffer. Årsagen er at programmet gentagne gange skal vente på at disken kommer forbi læsehovedet. Med buffering læses en forholdsvis stor portion af disken ind i bufferen når lejlighed gives, og efterfølgende tilgang til disse data fra programmet kræver derfor ikke fysisk læsning på selve disken.

Når vi har indført buffering skal vi være opmærksomme på få disken fuldt opdateret inden programmet lukkes ned. Dette sker når vi lukker filen, eller når vi eksplicit beder om det ved at kalde f.eks. C funktionen fflush fra stdio.h.

Figur 44.1    En illustration af filbuffere mellem den ydre enhed og programmet

 

44.3.  Strukturen FILE
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

En fil i et C program er i praksis en pointer til en FILE structure. Structures blev behandlet i kapitel 39. Vi vil ikke her beskrive de enkelte felter i FILE strukturen. Vi gør en dyd ud af ikke at gøre os afhængige af denne viden. I den forstand opfatter vi filer som objekter i en abstrakt datatype, jf. kapitel 42.

Strukturen FILE beskriver egenskaberne af en fil, herunder det afsatte bufferområde

Helt overordnet er følgende blandt egenskaberne af FILE:

  • Den nuværende filposition

  • Detaljer om filbufferen

  • Andre interne aspekter

Når talen er om filens øjeblikkelige tilstand er den nuværende filposition af stor betydning. Det er vigtigt at forstå, at i takt med at vi læser en sekventiel fil vil filpositionen gradvis forøges.

Programmøren udnytter normalt ikke kendskab til detaljerne i FILE

FILE og de tilknyttede operationer er et eksempel på en abstrakt datatype

 

44.4.  Simpel skrivning og læsning af en fil
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi vender os nu mod små, illustrative eksempler. Vi ser først på tegnvis skrivning og læsning. Eksemplerne i program 44.1, program 44.2 og senere program 44.4 arbejder alle på en fast fil, som vi kalder "first-file".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(void) {

  FILE *output_file_pointer;
  char *str = "0123456789abcdefghijklmnopqrstuvw";
  int i;

  output_file_pointer = fopen("first-file", "w");

  while (*str != '\0'){
    fputc(*str, output_file_pointer);
    str++;
  }

  fclose(output_file_pointer);
  return 0;
}
Program 44.1    Skrivning af tegn fra en tekststreng på en tekstfil.

Vi diskuterer her program 44.1. Overordnet skriver programmet - tegn for tegn - strengen str på filen first-file. På det blå sted erklærer vi variablen ouput_file_pointer som en pointer til FILE. På det første røde sted åbner vi filen i skrive mode, "w" for write. While-løkken gennemløber tegnene i str, og ved brug af standard io funktionen fputc skrives hvert tegn i str til output filen. Vi erindrer *str dereferencer pointeren - dvs. at vi tilgår hvert enkelt tegn i strengen gennem pointeren. (Dereferencing er beskrevet i afsnit 22.4). str++ tæller pointeren op pr. pointer aritmetik, jf. afsnit 24.5. Afsluttende, på det sidste røde sted, lukker vi filen.

Når vi kører program 44.1 får vi lavet first-file. I program 44.2 læser vi denne fil og putter tegnene tilbage i en tekststreng.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#define MAX_STR_LEN 100

int main(void) {

  FILE *input_file_pointer;
  char str[MAX_STR_LEN];
  int ch;
  int i = 0;

  input_file_pointer = fopen("first-file", "r");

  while ((ch = fgetc(input_file_pointer)) != EOF){
    str[i] = ch;
    i++;
  }
  str[i] = '\0';

  printf("Read from file: %s\n", str);

  fclose(input_file_pointer);
  return 0;
}
Program 44.2    Læsning af tegn fra en tekstfil til en tekststreng.

Vi forklarer nu program 44.2. Ligesom i program 44.1 erklærer vi en FILE* variabel, og vi åbner filen, dog her i læse mode. I en while-løkke læser vi tegnene i filen, indtil vi møder EOF. Det boolske udtryk i while-løkken er et typisk C mønster som kalder fgetc, assigner til variablen ch, og tester om det læste tegn er forskelligt fra EOF. Bemærk at ch er erklæret som en int, ikke en char. Årsagen er at EOF ikke er en gyldig char værdi, men derimod typisk -1. Et kald af fgetc læser netop ét tegn. De læste tegn lægges i arrayet str. Vi afslutter med lukning af filen.

Det er altid en god ide at placere filåbning og fillukning i samme funktion, idet det sikrer (langt hen ad vejen i det mindste) at en åben fil altid bliver lukket. Åbning og lukning optræder således altid i par.

I program 44.3 viser vi et program fra lærebogen, som laver dobbelt linieafstand i en tekstfil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>

void   double_space(FILE *ifp, FILE *ofp);
void   prn_info(char *pgm_name);

int main(int argc, char **argv)
{
   FILE   *ifp, *ofp;

   if (argc != 3) {
      prn_info(argv[0]);
      exit(1);
   }
   ifp = fopen(argv[1], "r");     /* open for reading */
   ofp = fopen(argv[2], "w");     /* open for writing */
   double_space(ifp, ofp);
   fclose(ifp);
   fclose(ofp);
   return 0;
}

void  double_space(FILE *ifp, FILE *ofp)
{
   int  c;

   while ((c = fgetc(ifp)) != EOF) {
      fputc(c, ofp);
      if (c == '\n')
       fputc('\n', ofp); /* found newline - duplicate it */
   }
}  

void prn_info(char *pgm_name)
{
   printf("\n%s%s%s\n\n%s%s\n\n",
      "Usage:  ", pgm_name, "  infile  outfile",
      "The contents of infile will be double-spaced ",
      "and written to outfile.");
}
Program 44.3    Et program der laver dobbelt linieafstand i en tekstfil.

Programmet accepterer to programparametre, jf. afsnit 31.3. Et typisk kald af programmet er således

  dbl_space infile outfile
forudsat at det oversatte program placeres i filen dbl_space. Det kan gøres med en -o option til gcc compileren. Filåbning og lukning er vist med blåt. Med brunt vises kaldet af den centrale funktion double_space, der laver arbejdet. I denne funktion læses et tegn med fgetc, og det skrives med fputc. Med rødt ser vi en test for at variablen c er newline tegnet. Når dette tegn mødes udskrives det endnu engang. Dermed opnås effekten af dobbelt linieafstand.

Når en fil er læst til ende vil fgetc og andre tilsvarende funktioner returnere EOF værdien

 

44.5.  Standard input, output og error
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

I dette afsnit skærper vi vores forstand af begreberne standard input og standard output.

De tre filer stdin, stdout, og stderr åbnes automatisk ved program start

Hver af stdin, stdout, og stderr er pointere til FILE

  • Standard input - stdin

    • Default knyttet til tastaturet

    • Kan omdirigeres til en fil med file redirection

  • Standard output - stdout

    • Default knyttet til skærmen

    • Kan omdirigeres til en fil med file redirection

  • Standard error - stderr

    • Default knyttet til skærmen

    • Benyttes typisk hvis stdout ikke tåler output af fejlmeddelelser

File redirection er illustreret i program 16.11.

Der findes en række behændige filfunktioner, som implicit arbejder på stdin og stdout i stedet for at få overført en pointer til en FILE structure

Bemærkning i rammen ovenfor er f.eks. rettet mod funktionerne printf, scanf, putchar, og getchar.

 

44.6.  Fil opening modes
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi skal have helt styr på et antal forskellige opening modes af filer i C.

Foruden læse- og skrivemodes tilbydes yderligere et antal andre opening modes

r Åbne eksisterende fil for input
w Skabe ny fil for output
a Skabe ny fil eller tilføje til eksisterende fil for output
r+ Åbne eksisterende fil for både læsning og skrivning. Start ved begyndelse af fil.
w+ Skabe en ny fil for både læsning og skrivning
a+ Skabe en ny fil eller tilføje til eksisterende fil for læsning og skrivning
Tabel 44.1    En tabel der beskriver betydningen af de forskellige opening modes af filer.

Opening modes "r" og "w" er enkle at forstå. Bemærk at med opening mode "w" skabes en ny fil, hvis filen ikke findes i forvejen. Hvis filen findes i forvejen slettes den! Opening mode "a" er bekvem for tilføjelse til en eksisterende fil. Som vi vil se i program 44.4 sker tilføjelsen i bagenden af filen. Heraf naturligvis navnet 'append mode'.

Opening mode "r+" er ligesom "r", blot med mulighed for skrivning side om side med læsningen. Dette virker i Unix, men ikke i Windows og Dos. Der er nærmere regler for kald af (eksempelvis) fflush mellem læsninger og skrivninger. Vi beskriver ikke disse nærmere her. Dette illustreres i program 44.5.

Opening mode "w+" er ligesom "w" med mulighed for læsning side om side med skrivning.

I program 44.4 viser vi tilføjning af "Another string" til indholdet af "first-file". Bemærk brug af "a" opening mode.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(void) {

  FILE *output_file_pointer;
  char *str = "Another string";
  int i;

  output_file_pointer = fopen("first-file", "a");

  while (*str != '\0'){
    fputc(*str, output_file_pointer);
    str++;
  }

  fclose(output_file_pointer);
  return 0;
}
Program 44.4    Tilføjelse af tegn til en tekstfil.

Programmet i program 44.5 benytter opening mode "r+". For hver læsning skrives et 'X' tegn. Det betyder at hvert andet tegn i filen bliver 'X' tegnet. Så hvis first-file indledningsvis indeholder strengen "abcdefg", og vi dernæst kører programmet, bliver filindholdet af first-file ændret til at være "aXcXeXgX". Variablen str ender med at have værdien "aceg", som udskrives på standard output.

Som allerede omtalt virker dette program kun på Unix, idet der er begrænsninger for skrivning i en fil i "r" modes i Windows og Dos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#define MAX_STR_LEN 100

int main(void) {

  FILE *input_file_pointer;
  char str[MAX_STR_LEN], ch;
  int i = 0;

  input_file_pointer = fopen("first-file", "r+");

  while ((ch = fgetc(input_file_pointer)) != EOF){
    str[i] = ch;

    fflush(input_file_pointer);  
    fputc('X', input_file_pointer);
    fflush(input_file_pointer);  

    i++;
  }
  str[i] = '\0';

  printf("Read from file: %s\n", str);

  fclose(input_file_pointer);
  return 0;
}
Program 44.5    Læsning og skrivning af fil med r+ (kun på Unix).

C muliggør åbning af binære filer ved at tilføje et b til opening mode

I C på Unix er der ikke forskel på binære filer og tekstfiler

 

44.7.  Funktioner på sekventielle filer
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Vi giver i dette afsnit en oversigt over de mest basale tegn-orienterede funktioner, som virker på sekventielle filer i C.

  • int fgetc(FILE *stream)

    • Læser og returnerer næste tegn fra stream. Returnerer EOF hvis der er end of file.

  • int fputc(int c, FILE *stream)

    • Skriver tegnet c til stream.

  • int ungetc(int c, FILE *stream)

    • Omgør læsningen af c. Tegnet c puttes altså tilbage i stream så det kan læses endnu en gang. Kun én pushback understøttes generelt.

  • char *fgets(char *line, int size, FILE *stream)

    • Læser en linie ind i line, dog højst size-1 tegn. Stopper ved både newline og EOF. Afslutter line med '\0' tegnet.

  • int fputs(const char *s, FILE *stream)

    • Skriver strengen s til stream, uden det afsluttende '\0' tegn.

Læsere af C by Dissection kan slå disse og en del af andre funktioner op i appendix A.

 

44.8.  Funktioner på random access filer
Indhold   Op Forrige Næste   Slide Aggregerede slides    Stikord Programindeks Opgaveindeks 

Ligesom for sekventielle filer i afsnit 44.7 viser i i dette afsnit de væsenligste funktioner på random access filer i C.

Begrebsligt kan man tilgå en random access fil på samme måde som et array

Følgende funktioner er centrale for random access filer:

  • int fseek(FILE *fp, long offset, int place)

    • Sætter filpositionen for næste læse- eller skriveoperation

    • offset er en relativ angivelse i forhold til place

    • place er SEEK_SET, SEEK_CUR, SEEK_END svarende til filstart, nuværende position, og filafslutning

  • long ftell(FILE *fp)

    • Returnerer den nuværende værdi af filpositionen relativ til filstart

  • void rewind(FILE *fp)

    • Ækvivalent til fseek(fp, 0L, SEEK_SET)

Vi illustrerer random access filer ved et af lærebogens eksempler, nemlig læsning af en fil baglæns. Altså læsning stik modsat af sekventiel læsning.

I program 44.6 prompter vi brugeren for filnavnet. Efter filåbning placerer vi os ved sidste tegn i filen. Dette sker med det første røde fseek. Vi går straks ét tegn tilbage med det andet røde fseek. I while-løkken læser vi tegn indtil vi når første position i filen. For hvert læst tegn går vi to trin tilbage. Husk at læsning med getc bringer os ét tegn frem. Kaldet af getc(ifp) er ækvivalent med fgetc(ifp). Det læste tegn udskrives på standard output med putchar. Det første tegn skal behandles specielt af det tredje røde fseek kald. Årsagen er naturligvis, at vi ikke skal rykke to pladser tilbage på dette tidspunkt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

#define   MAXSTRING   100

int main(void){
   char   filename[MAXSTRING];
   int    c;
   FILE   *ifp;

   fprintf(stderr, "\nInput a filename:  ");
   scanf("%s", filename);
   ifp = fopen(filename, "r");     
   fseek(ifp, 0, SEEK_END); 
   fseek(ifp, -1, SEEK_CUR);
   while (ftell(ifp) > 0) {
      c = getc(ifp);        
      putchar(c);
      fseek(ifp, -2, SEEK_CUR); 
   }
   /*  The only character that has not been printed
       is the very first character in the file. */

   fseek(ifp, 0, SEEK_SET);
   c = getc(ifp);
   putchar(c);
   return 0;
}
Program 44.6    Et program der læser en fil baglæns.

Genereret: Onsdag 7. Juli 2010, 15:13:25
Thema indholdsfortegnelse -- Tastaturgenvej: 'u'  Forrige tema i denne lektion -- Tastaturgenvej: 'p'  Næste slide i denne lektion -- Tastaturgenvej: 'n'