Kapitel 11
Input/Output og Filer

Kurt Nørmark
Institut for Datalogi, Aalborg Universitet


Sammendrag
Forrige lektion Næste lektion
Stikord Referencer Indhold
I denne lektion beskriver vi filbegrebet og de forskellige operationer på filer. Vi slutter af med en diskussion af hvordan structures can udskrives på filer og indlæses fra filer.


Introduktion til filer

Oversigt over filbegreber
Slide Indhold Stikord
Referencer 

En fil er en samling af data, typisk lagret på et ekstert og permanent lagermedium

  • Datatilgang:

    • Sekventiel

    • Random access

  • Dataindhold:

    • Tegn

      • Tekstfiler der indeholder bytes der tolkes som tegn fra ASCII alfabetet

    • Andre data enheder

      • Binære filer der indeholder bitmønstre fra andre datatyper (eksempelvis structs)

Filer er ressourcer som administreres af et operativsystem

Fra et programmeringssprog er det muligt at knytte en forbindelse til en fil i operativsystemet

Sekventielle filer
Slide Indhold Stikord
Referencer 

Sekventielle filer er modelleret efter egenskaberne af strengt sekventielle medier, såsom magnetbånd

Begrebet sekventiel fil: En sekventiel fil læses altid i den rækkefølge den er skrevet. Rækkefølgen af enheder i en sekventiel fil afspejler direkte rækkefølgen af de udførte skriveoperationer på filen.

  • Mode of operation

    • En sekventiel fil tilgås enten i læse mode eller skrive mode

    • I det simple tilfælde kan man ikke både læse fra og skrive til en fil

  • Ydre enheder

    • Tastaturet modelleres typisk som en sekventiel fil i læse mode

      • Standard input

    • Skærmen modelleres typisk som en sekventiel fil i skrive mode

      • Standard output

Random access filer
Slide Indhold Stikord
Referencer 

Det er unaturligt at insistere på, at filer på en harddisk er sekventielle filer

Det er muligt og naturligt at kunne skrive og læse i en mere vilkårlig rækkefølge

På trods af dette tilgår vi typisk filer på harddiske sekventielt


Filer i C

Sekventielle filer i C
Slide Indhold Stikord
Referencer 

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

    • FILE *fopen(const char *filename, const char *mode)

    • Knytter forbindelsen til en fil i operativsystemets hierarki af filer

    • Angivelse af opening mode

    • Etablerer og returnerer en pointer til en FILE struct

  • Arbejde på filen

    • Sker ved brug af operationer fra stdio.h

    • Filen husker sin nuværende position

    • Operationerne på filen foregår relativt til den nuværende position

  • Lukning af filen

    • int fclose(FILE *stream)

    • Tømmer buffere og frigiver interne ressourcer

    • Bryder forbindelsen til filen i operativsystemet

Filbuffering
Slide Indhold Stikord
Referencer 

En buffer er et lagerområde mellem programmet og det fysiske lager

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

Funktion fflush tømmer en output buffer

Strukturen FILE
Slide Indhold Stikord
Referencer 

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

  • Egenskaberne i FILE strukturen

    • Den nuværende filposition

    • Detaljer om filbufferen

    • Andre interne aspekter

Programmøren udnytter normalt ikke kendskab til detaljerne i FILE

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

Simpel skrivning og læsning af en fil
Slide Indhold Stikord
Referencer 

Vi viser her elementære eksempler på skrivning og læsning af tegn fra tekstfiler i C

Program: Skrivning af tegn fra en tekststreng på en tekstfil.
#include <stdio.h>

int main(void) {

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

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

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

  fclose(output_file_pointer);
  return 0;
}

Program: Læsning af tegn fra en tekstfil til en tekststreng.
#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;
}

Henvisning

Program: Et program der laver dobbelt linieafstand i en tekstfil.
/* Adapted from the book 'C by Dissection' by Kelly and Pohl */
#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);    /* duplicate newline */
   }
}  

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.");
}

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

putc og getc svarer til hhv. fputc og fgetc, men er typisk implementeret som makroer i stedet for funktioner

EOF repræsenterer typisk heltallet -1

Standard input, output og error
Slide Indhold Stikord
Referencer 

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 f.eks. hvis stdout ikke tåler output af fejlmeddelelser

Henvisning

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

Eksempelvis svarer getchar() til getc(stdin) og putchar(ch) svarer til putc(ch,stdout)

Fil opening modes
Slide Indhold Stikord
Referencer 

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

Tabel. En tabel der beskriver betydningen af de forskellige opening modes af filer.
rÅbne eksisterende fil for input
wSkabe ny fil for output
aSkabe 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
 

Henvisning

Program: Tilføjelse af tegn til en tekstfil.
#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: Læsning og skrivning af fil med r+.
#include <stdio.h>
#define MAX_STR_LEN 100

int main(void) {

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

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

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

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

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

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

  fclose(file_pointer);
  return 0;
}

Program: first-file efter at ovenstående program er kørt.
0X2X4X6X8XaXcXeXgXiXkXmXoXqXsXuXwXnXtXeX XtXiXgX

Program: Standard output fra ovenestående program.
Read from file: 02468acegikmoqsuwnte tig

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

Detaljer om fil opening modes
Slide Indhold Stikord
Referencer 

En mere detaljeret og nuanceret forståelse af forskelle og ligheder mellem fil opening modes

Tabel. En tabel der i størrel detalje beskriver betydningen af de forskellige opening modes af filer.
rwar+w+a+
Filen skal eksistere på forhåndja nejnejja nejnej
Filindhold tabesnejja nejnejja nej
Læsning tilladtja nejnejja ja ja
Skrivning tilladtnejja ja ja ja ja
Skrivning i bagende af filnejnejja nejnejja
 

Tabellen er reproduceret fra 'C - A Reference Manual' af Harbison og Steele

Funktioner der arbejder på sekventielle filer
Slide Indhold Stikord
Referencer 

Vi viser en oversigt over udvalgte funktioner, som processerer filer sekventielt

  • int fgetc(FILE *stream)

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

  • int ungetc(int c, FILE *stream)

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

  • int fputc(int c, FILE *stream)

    • Skriver tegnet c til stream. Returnerer c, eller EOF.

  • 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.

  • int feof(FILE *stream)

    • Returnerer hvorvidt en tidligere kaldt funktion har signaleret en EOF betingelse.

Opgave 11.3. Læsning af k ord fra fil

Skriv en funktion

  int getwords(FILE *ifp, int k, char *words)

der, om muligt, læser k ord fra tekstfilen peget på af ifp. Ordene skal læses over i tekststrengen words (som allokeres af den funktion, som kalder getwords). Hvis det ønskes kan ordene organiseres linievis, eller blot med 'white space' imellem ordene.

Den programmerede funktion skal returnere det faktiske antal af læste ord (som kan være mindre end k).

Opgave 11.3. En simpel grep funktion

grep er en Unix kommando der søger efter linier i en tekstfil, som matcher en søgestreng. I denne opgave vil vi programmere en meget simpel variant af grep, som udskriver de tekstlinier, i hvilke et bestemt søgeord forekommer.

Vi ønsker at anvende det udviklede program således fra kommandolinien:

  search programmering my-file.txt

eller måske snarere:

  ./search programmering my-file.txt

Dette skal udskrive alle de liner i my-file.txt som indholder strengen "programmering".

Programmet skal anvende program parametre, som forklaret på denne slide.


Directories i C

Directories i C
Slide Indhold Stikord
Referencer 

Directories (kataloger/mapper) kan håndteres via brug af funktioner fra dirent.h biblioteket

dirent.h er et psuedo-standard bibliotek for C

  • Udvalgte funktioner fra dirent.h

    • Typerne DIR og struct dirent

    • DIR *opendir(const char* dirname)

    • struct dirent* readdir(DIR* dirp)

    • void rewinddir(DIR* dirp)

    • int closedir(DIR* dirp)

Henvisning

Program: Et list directory program - lånt fra Wikepedia.
/**************************************************************
 * A simpler and shorter implementation of ls.
 * Adapted from Wikipedia.
 **************************************************************/

#include <stdio.h>
#include <dirent.h>
 
int listdir(const char *path) {
  struct dirent *entry;
  DIR *dp;
 
  dp = opendir(path);
  if (dp == NULL) {
    perror("opendir");
    return -1;
  }
 
  while((entry = readdir(dp)))
    puts(entry->d_name);
 
  closedir(dp);
  return 0;
}
 
int main(int argc, char **argv) {
  int counter = 1;
 
  if (argc == 1)
    listdir(".");
  else if (argc == 2)
    listdir(argv[1]);
  else printf("Usages: ls    or   ls dirPath\n");
 
  return 0;
}

Funktioner på random access filer
Slide Indhold Stikord
Referencer 

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

  • Random access funktioner:

    • 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)

Program: Et program der læser en fil baglæns.
/* Echoes a file, in reverse, to standard output */

#include <stdio.h>

#define   MAXSTRING   100

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

   // Prompt for file name:
   fprintf(stderr, "\nInput a filename:  ");
   scanf("%s", filename);

   // Read chars from file in reverse order:
   ifp = fopen(filename, "r");     
   fseek(ifp, 0, SEEK_END); 
   fseek(ifp, -1, SEEK_CUR);       // Now ready to read last char
   while (ftell(ifp) > 0) {        
      c = getc(ifp);        
      putchar(c);                  // Echo on standard output
      fseek(ifp, -2, SEEK_CUR);    
   }

   // The only character that has not been printed
   // is the very first character in the file. Handle it now:
   fseek(ifp, 0, SEEK_SET);
   c = getc(ifp);
   putchar(c);

   // Close file:
   fclose(ifp);
   return 0;
}


Formateret output og input

Formateret output - printf familien (1)
Slide Indhold Stikord
Referencer 

Familien af printf funktioner tilbyder tekstuel output af værdier i forskellige typer, og god kontrol af output formatet

Familien omfatter funktioner der skriver på en bestemt fil, på standard output, og i en streng

  • Formålet med printf

    • Pretty printing

    • Konvertering af værdier fra forskellige typer til tekst

    • Kontrol af det tekstuelle format

  • Tre varianter i printf familien:

    • printf(kontrolstreng, ...)

    • fprintf(fil, kontrolstreng, ...)

    • sprintf(streng, kontrolstreng, ...)

Formateret output - printf familien (2)
Slide Indhold Stikord
Referencer 

Vi ser her på fortolkningen af en kontrolstreng via et eksempel

Figur. Nedbrydning af kontrolstrengen for %-#08.3hd. Denne figur er inspireret fra 'A C Reference Manual' af Harbison & Steele

  • %-#08.3hd

    • Conversion letter: d for heltalsformatering

    • Flags: - left justify, # use variant (ikke anvendelig her), 0 zero padding

    • Min. field width: 8. Mindste plads som afsættes

    • Precision: 3. Mindste antal cifre der udskrives - bruger 0 paddings om nødvendig

    • Size modifier: h short

Formateret output - printf familien (3)
Slide Indhold Stikord
Referencer 

Lærebogen - og referencebøger om C - har mange detaljer om printf

  • Udvalgte bemærkninger om printf funktionerne

    • Returnerer antallet af udskrevne tegn

    • Man kan kontrollere højre og venstre justering i felt

      • Et - flag betyder venstrejustering

    • Field width og precision kan overføres som parametre til printf

      • Angivelse med *

Program: Udskrivning af heltal: Illustration af *.* field width og precision .
#include <stdio.h>

int main (int argc, char *argv []){

  int int_number, n, m, printf_res;

  while (1){
   printf("Enter an integer, min. field width, and precision\n");
   scanf(" %d %d %d", &int_number, &n, &m);
   printf_res = printf("%*.*d\n", n, m, int_number);
   printf("printf returned: %d\n", printf_res);
  }

}

Program: Udskrivning af doubles: Illustration af *.* field width og precision .
#include <stdio.h>

int main (int argc, char *argv []){

  int n, m, printf_res;
  double double_number;

  while (1){
   printf("Enter a double, min. field width, and precision\n");
   scanf(" %lf %d %d", &double_number, &n, &m);
   printf_res = printf("%*.*f\n", n, m, double_number);
   printf("printf returned: %d\n", printf_res);

  }

}

Viden om printf tilegnes efter behov

Formateret input - scanf familien (1)
Slide Indhold Stikord
Referencer 

Familien af scanf funktioner tilbyder tekstuel input (parsing, scanning) af værdier i forskellige typer

Familien omfatter funktioner der læser fra en bestemt fil, fra standard input, og fra en streng

  • Formålet med scanf

    • Fortolkning og parsing af tekst

    • Konvertering af tekstudsnit til værdier i forskellige typer

    • Assignment af værdier til parametre overført pr. reference - call by reference parametre

  • Tre varianter i scanf familien:

    • scanf(kontrolstreng, ...)

    • fscanf(fil, kontrolstreng, ...)

    • sscanf(streng, kontrolstreng, ...)

Parsing er vanskeligere end pretty printing

Formateret input - scanf familien (2)
Slide Indhold Stikord
Referencer 

Begreber som knytter sig til formateret input med scanf

  • Kontrolstreng med et antal direktiver

  • Tre slags direktiver

    • Almindelige tegn: Matcher de angivne tegn

    • White space: Matcher så mange mellemrum, tabs, og linieskift som mulig

    • Konverteringsspecifikation: %...c:
      Matcher input efter givne regler og konverteres til en værdi som angivet af c

  • Scan field:

    • Et sammenhængende område i inputtet som forsøges konverteret med en given konverteringsspecifikation.

    • Scan width: En angivelse i konverteringsspecifikationen af den maksimale længde af et scan field

    • Består af et område uden white space (pånær for %c)

Program: Illustration af directives med almindelige tegn.
/* Illustrates match of 'ordinary' characters in the control string */

#include <stdio.h>

int main (int argc, char *argv []){

  int scan_res;
  double double_number = 0;

  printf("Enter a dollar char followed by a double\n");
  scan_res = scanf("$%lf", &double_number);
  printf("Number read: %f\n", double_number);
  printf("scanf returned: %d\n", scan_res);
}

Program: Illustration af brugen af scan width ved læsning af en streng.
/* Illustrates use of scan width - reading a text string */

#include <stdio.h>

int main (int argc, char *argv []){

  int scan_res;
  char str[20];

  printf("Enter a test string - at most 6 chars will be read\n");
  scan_res = scanf("%6s", str);
  printf("This string has been read: %s\n", str);
  printf("scanf returned: %d\n", scan_res);
}

Program: Illustration af brugen af scan width ved læsning af en double.
/* Illustrates use of scan width - reading a double */

#include <stdio.h>

int main (int argc, char *argv []){

  int scan_res;
  double d;

  printf("Enter a double - at most 6 chars will be read\n");
  scan_res = scanf(" %6lf", &d);
  printf("This double has been read: %f\n", d);
  printf("scanf returned: %d\n", scan_res);
}

Formateret input - scanf familien (3)
Slide Indhold Stikord
Referencer 

Nogle væsentlige bemærkninger om scanf funktionerne

  • Returværdi

    • EOF hvis input 'løber tør' - input failure

    • Antallet af gennemførte konverteringer (indtil evt. matching failure)

  • Assignment suppression *

    • Matching - men ingen assignment til pointervariable

    • Tæller ikke som en konvertering

    • Angives lige efter % tegnet

  • Scan set af formen [cde]

    • Indlæsning af en streng fra alfabetet bestående af 'c', 'd' og 'e'.

  • Scan set af formen [^cde]

    • Indlæsning af en streng afsluttet af et tegn fra alfabetet bestående af 'c', 'd' og 'e'.

Formateret input - scanf familien (4)
Slide Indhold Stikord
Referencer 

Vi viser et antal eksempler på brug af scanf og scan sets

Program: Et program der læser ikke-blanke liner fra en fil og udskriver disse på standard output.
#include <stdio.h>

int main(int argc, char *argv[]){

  FILE *ifp = fopen(argv[1],"r");

  char line[300];
  int i = 0;

  while (fscanf(ifp," %[^\n]", line) == 1){    // Space: Skip white space, and
                                               // Scan until new line
    printf("%s\n", line);
    i++;
  }

  printf("\n\ni = %i\n",i);

  fclose(ifp);
  
  return 0;
}

Program: Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra stdin.
#include <stdio.h>

int main(void) {

  char alph[25], numb[25];
  int numres;

  printf("Enter input, such as \"aaa111bbb222\"\n");

  while (scanf("%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    numres = scanf("%[0123456789]", numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}

Program: Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra en streng - virker ikke.
/* Does NOT work!  Reports repeated MISMATCHes */

#include <stdio.h>

int main(void) {

  char *str = "abc135def24681ghi3579";
  char alph[25], numb[25];
  int numres;

  while (sscanf(str,"%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    numres = sscanf(str,"%[0123456789]", numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}

Program: Et andet program der adskiller alfabetiske og numeriske afsnit i en tekst - læser fra en streng - virker.
/* Works as intended */

#include <stdio.h>
#include <string.h>

int main(void) {

  char *str = "abc135def24681ghi3579";
  char alph[25], numb[25];
  int numres;

  while (sscanf(str,"%[abcdefghijklmnopqrstuvwxyz]", alph) == 1){
    str += strlen(alph);

    numres = sscanf(str,"%[0123456789]", numb);
    str += strlen(numb);

    if (numres != 1) 
       printf("MISMATCH");
    else
       printf("Alphabetic: %s\n"
              "Numeric: %s\n\n",
              alph, numb);
  }
  
  return 0;
}


Input og output af structures

Input/Output af structures (1)
Slide Indhold Stikord
Referencer 

Det er ofte nyttigt at udskrive en eller flere structs på en fil, og tilsvarende at kunne indlæse en eller flere structs fra en fil

Væsenligste egenskab: En udskrevet struct s skal genindlæses som en struct, der er magen til s

  • Tekstfil

    • Tekstuel encode og decode af en struct

    • F.eks. én linje pr. struct

  • Binær fil

    • Direkte input/output af de binære data i en struct

Input/Output af structures (2)
Slide Indhold Stikord
Referencer 

Vi viser en linje-orienteret løsnning på en tekstfil

Program: Programmet der udskriver bøger på en output fil.
#include <stdio.h>
#include "book-read-write.h"

int main(void) {

  book *b1, *b2;
  FILE *output_file;

  b1 = make_book("C by Dissection", "Kelly and Pohl", 
                  "Addison Wesley", 2002, 1);
  b2 = make_book("The C Programming Language", 
                  "Kernighhan and Ritchie",
                  "Prentice Hall", 1988, 1);

  output_file = fopen("books.dat", "w");

  print_book(b1, output_file);
  print_book(b2, output_file);

  fclose(output_file);

  return 0;

}

Program: Den skrevne fil.
C@by@Dissection Kelly@and@Pohl Addison@Wesley 2002 1
The@C@Programming@Language Kernighhan@and@Ritchie Prentice@Hall 1988 1

Program: Programmet der indlæses bøger fra en input fil.
#include <stdio.h>
#include "book-read-write.h"

int main(void) {

  book *b1, *b2;

  FILE *input_file;
  input_file = fopen("books.dat", "r");

  b1 = read_book(input_file);
  b2 = read_book(input_file);

  prnt_book(b1);   prnt_book(b2);

  fclose(input_file);

  return 0;

}

Program: Output fra programmet - på skærmen.
Title: C by Dissection
Author: Kelly and Pohl
Publisher: Addison Wesley
Year: 2002
University text book: yes

Title: The C Programming Language
Author: Kernighhan and Ritchie
Publisher: Prentice Hall
Year: 1988
University text book: yes

Program: Header filen book-read-write.h.
#define PROTECTED_SPACE '@'
#define PROTECTED_NEWLINE '$'
#define BUFFER_MAX 1000

struct book {
  char *title, *author, *publisher;
  int publishing_year;
  int university_text_book;
};

typedef struct book book;

book *make_book(const char *title, const char *author, 
                const char *publisher, 
		int year, int text_book);

void prnt_book(book *b);

void print_book(book *b, FILE *ofp);

book *read_book(FILE *ifp);

Program: Implementationen af biblioteket - book-read-write.c.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "book-read-write.h"

/* Allocated memory to a book, and allocate strings to initialized by the 
   string constants passed as parameter */
book *make_book(const char *title, const char *author, const char *publisher, 
               int year, int text_book){
  static book *result;
  result = (book*)malloc(sizeof(book));
  //  strcpy(result->title,title);

  /* Copy string constants to dynamically allocated strings */
  result->title = strcpy((char *)calloc(strlen(title)+1,sizeof(char)), 
                         title);
  result->author = strcpy((char *)calloc(strlen(author)+1,sizeof(char)), 
                         author);
  result->publisher = strcpy((char *)calloc(strlen(publisher)+1,sizeof(char)), 
                         publisher); 
  result->publishing_year = year;
  result->university_text_book = text_book;
 
  return result;
}

/* print book b to standard output */
void prnt_book(book *b){
  char *yes_or_no;

  yes_or_no = (b->university_text_book ? "yes" : "no"); 
  printf("Title: %s\n"
         "Author: %s\n"
         "Publisher: %s\n"
         "Year: %4i\n"
         "University text book: %s\n\n",
         b->title, b->author, b->publisher, 
         b->publishing_year, yes_or_no);
}

char *white_space_protect(char *str){
  int str_lgt = strlen(str), i, j;
  for(i = 0; i < str_lgt; i++){
    if (str[i] == ' ')
      str[i] = PROTECTED_SPACE;
    else if (str[i] == '\n')
      str[i] = PROTECTED_NEWLINE;
  } 
  return str;
} 

char *white_space_deprotect(char *str){
  int str_lgt = strlen(str), i;
  for(i = 0; i < str_lgt; i++){
    if (str[i] == PROTECTED_SPACE)
      str[i] =  ' ';
    else if (str[i] == PROTECTED_NEWLINE)
      str[i] = '\n';
  }
  return str;
} 

/* Encode the book pointed to by p in the string str */
void encode_book(book *b, char *str){
  sprintf(str, "%s %s %s %i %i\n", 
          white_space_protect(b->title), 
          white_space_protect(b->author), 
          white_space_protect(b->publisher), 
          b->publishing_year, b->university_text_book);
} 

book *decode_book(char *str){
  char book_title[100], book_author[100], book_publisher[100];
  int book_year, book_uni;

  sscanf(str, "%s %s %s %i %i", 
         book_title, book_author, book_publisher,
         &book_year, &book_uni);
    
  return make_book(white_space_deprotect(book_title), 
                   white_space_deprotect(book_author),
                   white_space_deprotect(book_publisher), 
                   book_year, book_uni);
}


void print_book(book *b, FILE *ofp){
  char buffer[BUFFER_MAX];
  encode_book(b, buffer);
  fprintf(ofp, "%s", buffer);
}

book *read_book(FILE *ifp){
  char buffer[BUFFER_MAX];
  fgets(buffer, BUFFER_MAX, ifp);
  return decode_book(buffer);
}

Program: Compilering af programmerne.
gcc -c book-read-write.c

gcc book-write-prog.c book-read-write.o -o book-write-prog

gcc book-read-prog.c book-read-write.o -o book-read-prog

Opgave 11.4. Input og Output af structsVi vil antage at vi har en struct som beskriver data om en person, så som
  struct person {
    char *name;
    int age;
    char sex;
  }
hvor sex er enten tegnet 'm' eller 'f'. I denne øvelse bliver du bedt om at programmere funktioner, som kan udskrive et antal personer på en fil, og som efterfølgende kan indlæse disse igen.

Konkret, skal der skrives to funktioner

    print_person(person *p, FILE *ofp)
    person *read_person(FILE *ifp)

Du kan vælge en tekst-baseret og linie-orienteret fremgangsmåde, som illustreret ved forelæsningen. Som et noget lettere alternativ kan du vælge at anvende en binær fremgangsmåde ved brug af fwrite og fread.

Overvej hvad der skal til for at anvende fwrite og freadstruct person.

Input/Output af structures (3)
Slide Indhold Stikord
Referencer 

Vi ser nu på generelle problemstillinger af struct IO

  • Problemstillinger:

    • Kun felter af taltyper, char, og string håndteres

    • Pointers til andre typer end strenge kan ikke udskrives og indlæses

    • Evt. nestede structs og arrays kræver specialbehandling

    • Vi vælger en løsning med én struct per linie i en tekst fil

En mere generel anvendelig løsning ville være ønskelig

Nogle sprog understøtter en meget simpel, binær file of records for dette behov

I C er funktionerne fread og fwrite nyttige for læsning og skrivning af binære filer

Binær input/output med fread og fwrite
Slide Indhold Stikord
Referencer 

Structures i C med simple aritmetiske typer kan let udskrives og indlæses binært med fwrite og fread

  • fwrite(pointerTilStruct, størrelseAfStruct, antal, fil)

    • Skriver et antal structs fra et array til en fil

    • Udskriver blot bitmønstret i en struct

    • Returnerer antallet af skrevne structs

  • fread(pointerTilStruct, størrelseAfStruct, antal, fil)

    • Læser et antal structs fra en fil til elementer i det array, som udpeges af første parameter

    • Læser de rå bits

    • Returnerer antallet af læste structs

Eksempler: Binær input/output med fread og fwrite
Slide Indhold Stikord
Referencer 

Eksempler på anvendelse af fwrite og fread

Program: Skrivning af en struct til en binær fil.
#include <stdio.h>

struct str {
  int f1;
  double f2;
  char f3;
  char f4[4];
};

int main(void){
  FILE *ofp;
  struct str rec= {5, 13.71, 'c', "xyz"};
  ofp = fopen("data", "w");

  fwrite(&rec, sizeof(struct str), 1, ofp);  
  fclose(ofp);
  return 0;
}

Program: Tilsvarende læsning af en struct fra en binær fil.
#include <stdio.h>

struct str {
  int f1;
  double f2;
  char f3;
  char f4[4];
};

int main(void){
  FILE *ifp;
  struct str rec;
  ifp = fopen("data", "r");

  fread(&rec, sizeof(struct str), 1, ifp);  

  printf("f1=%d, f2= %f, f3 = %c, f4 = %s\n",
         rec.f1, rec.f2, rec.f3, rec.f4);
  fclose(ifp);
  return 0;
}

Program: Output fra programmet - på skærmen.
f1=5, f2= 13.710000, f3 = c, f4 = xyz

Det giver ikke mening at udskrive og genindlæse pointere med fwrite og fread

Dette betyder at dynamisk allokerede strenge ikke kan håndteres af fread og fwrite

Program: Skrivning af en struct med et pointer felt til en binær fil.
#include <stdio.h>

struct str {
  int f1;
  double f2;
  char f3;
  char *f4;
};

int main(void){
  FILE *ofp;
  struct str rec= {5, 13.71, 'c', "xyz"};
  ofp = fopen("data", "w");

  fwrite(&rec, sizeof(struct str), 1, ofp);  
  fclose(ofp);
  return 0;
}

Program: Tilsvarende læsning af en struct fra en binær fil - virker ikke.
#include <stdio.h>

struct str {
  int f1;
  double f2;
  char f3;
  char *f4;
};

int main(void){
  FILE *ifp;
  struct str rec;
  ifp = fopen("data", "r");

  fread(&rec, sizeof(struct str), 1, ifp);  

  printf("f1=%d, f2= %f, f3 = %c, f4= %s\n", rec.f1, rec.f2, rec.f3, rec.f4);
  fclose(ifp);
  return 0;
}

Program: Output fra programmet - på skærmen.
f1=5, f2= 13.710000, f3 = c, f4= ( 

Opgaver
Slide Indhold Stikord
Referencer 

Opgave 11.5. Tynde matricer

En vilkårlig matrix kan generelt repræsenteres på en tekstfil, med én linie pr. række. Første linie kan angive antallet af rækker og søjler i matricen (altså to heltal). I denne opgave kalder vi dette for den generelle tekstfil repræsentation af vilkårlige matricer.

En tynd matrix er et array af to dimensioner, hvor mange af elementerne er nul.

I denne opgave vil skrive et program der undersøtter en speciel tekstfil repræsentation af tynde matricer på tekstfiler, som i visse tilfælde fylder mindre end den generelle repræsentation af matricer på tekstfiler.

I den nye repræsentation skal den første linie af tekstfilen indeholde dimensionerne af matricen - to heltal - antal rækker og antal søjler. Den anden linie skal angive antallet af elementer i matricen, som ikke er nul. Hver af de efterfølgende linier indholder tre tal: Rækkenummer, søjlenummer, og et tal fra matricen (som ikke er nul).

Skriv først en funktion som læser en matrix fra den generelle repræsentation på en tekstfil, og som udskriver den specielle tynde repræsentation på en ny tekstfil.

Skriv dernæst en funktion som løser det omvendte problem: Læsning af en tynd matrix fra en tekstfil og skrivning af en tilvarende matrix på generel form på en anden tekstfil.

Som et eksempel svarer følgende generelle tekstfil repræsentation af matricen

4 3
0.0 0.0 1.0
0.0 0.0 0.0
2.0 0.0 0.0
0.0 3.0 0.0

til følgende specielle tekstfil repræsentation af matricen - som en tynd matrice

4 3
3
1 3 1.000000
3 1 2.000000
4 2 3.000000

Denne opgave svarer til opgave 4 side 679 i 6. udgave af lærebogen


Samlede referencer
Indhold Stikord
Læsning og skrivning af tegn fra tidligere lektion
File Redirection
fopen og opening modes
dirent.h - fra Wikipedia

 

Kapitel 11: Input/Output og Filer
Kursets hjemmeside     Forfatteren's hjemmeside     Om frembringelsen af disse sider     Forrige lektion (top)     Næste lektion (top)     Forrige lektion (bund)     Næste lektion (bund)     
Genereret: 13. november 2012, 16:02:21