Index over opgaver i denne lektion   Alfabetisk indeks   Kursets hjemmeside   

Opgaver og løsninger
Input/Output og Filer


13.1   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 der er udpeget af pointeren ifp. Ordene skal læses over i tekststrengen words (som allokeres af den funktion, som kalder getwords). Hvis det ønskes kan ordene organiseres linievis i words, 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).

Løsning

Her er min løsning:

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

int getwords(FILE *ifp, int k, char *words){
  char word[100];
  int count = 0;
  while (fscanf(ifp, "%s", word) != EOF && count < k){
    count++;
    strcpy(words,word);
    words += strlen(word); 
    *words = '\n';
     words++;
  }
  words--;
  *words = '\0';
  return count;
}
  

int main(void) {
  char buffer[500];
  int no_of_words;

  FILE *input_file = fopen("text-input","r");
  no_of_words = getwords(input_file, 5, buffer);
  printf("%i words read:\n%s\n", no_of_words, buffer);
  fclose(input_file);

  return 0;
}


13.2   En simpel grep funktion  

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

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

  search programmering my-file.txt

eller måske snarere:

  ./search programmering my-file.txt

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

Programmet skal anvende programparametre, som forklaret på denne slide.

Løsning

Her er en løsning på opgaven (som endvidere understøtter en -n option, der afstedkommer udskrift af linienumre):

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

#define MAXLINE 200

void grep(char *file_name, char* pattern, int print_line_numbers);

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

  char *pattern, *file_name;
  int line_numbers;  /* A boolean */

  /* Access the parameters of main */
  if (argc == 3) {
    file_name = argv[2]; line_numbers = 0; pattern = argv[1];
  }
  else if (argc == 4 && strcmp(argv[1], "-n") == 0) {
    file_name = argv[3]; line_numbers = 1; pattern = argv[2];
  }
  else {
    fprintf(stderr, "Calling form: search [-n] search_string file\n");
    exit(1);
  }

  grep(file_name, pattern, line_numbers);

  return 0;
}

/* Print those lines of file_name that contain pattern */
void grep(char *file_name, char* pattern, int print_line_numbers){  
  char line[MAXLINE];
  FILE *ifp;
  int n = 1;

  if ((ifp = fopen(file_name, "r")) == NULL){
    fprintf(stderr, "\nCannot open %s\n\n", file_name);
    exit(1);
  }

  while(fgets(line, MAXLINE, ifp) != NULL){
    if (strstr(line, pattern) != NULL){
      if (print_line_numbers)
        printf("%3i: %s", n, line);
      else
        printf("%s", line);
    }
    n++;
  }

  fclose(ifp);
}

Læg godt mærke til at vi ikke blot løser problemet i main. Vi skriver naturligvis en funktion, grep, som forsynes med passende parametre. I denne opgave kræves det, at grep funktionen udskriver tekst (linier med matchende tekststreng) på standard output.

Vi vil antage at programmet findes på kildefilen simple-grep.c. Oversættelse og kørsel (søgning i kildefilen selv) kan foregå på denne måde:

   > gcc simple-grep.c -o search
   > search.exe -n pattern simple-grep.c

Outputtet er:

  7: void grep(char *file_name, char* pattern, int print_line_numbers);
 11:   char *pattern, *file_name;
 16:     file_name = argv[2]; line_numbers = 0; pattern = argv[1];
 19:     file_name = argv[3]; line_numbers = 1; pattern = argv[2];
 26:   grep(file_name, pattern, line_numbers);
 31: /* Print those lines of file_name that contain pattern */
 32: void grep(char *file_name, char* pattern, int print_line_numbers){  
 43:     if (strstr(line, pattern) != NULL){

Dette output viser de linjer i kildefilen simple-grep.c (som vist ovenfor) der indholder ordet "pattern".


13.3   Input og Output af structs  

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

Hvis det er hensigtsmæssigt, er det OK at allokere name som et char array: char name[MAX_NAME_SIZE].

Konkret, skal der skrives to funktioner
    void print_person(struct 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.

Løsning

Her er en mulig løsning på opgaven som udskriver en person struct på en tekstfil.

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

#define PROTECTED_SPACE '@'
#define PROTECTED_NEWLINE '$'
#define BUFFER_MAX 1000

struct person {
  char *name;
  int age;
  char sex;
};

typedef struct person person;

/* Make and return a person */
person *make_person(const char *name, const int age, const char sex);

/* pretty print person on standard output */
void prnt_person(person *b);

/* print person for storage purposed on ofp */
void print_person(person *p, FILE *ofp);

/* retrieve person from ifp and return a pointer to it */
person *read_peson(FILE *ifp);


/* Allocated memory to a person, and allocate strings to be initialized by the 
   string constants passed as parameter */
person *make_person(const char *name, const int age, const char sex){
  static person *result;
  result = (person*)malloc(sizeof(person));

  /* Copy string constants to dynamically allocated strings */
  result->name = strcpy((char *)calloc(strlen(name)+1,sizeof(char)), 
			name);
  result->age = age;
  result->sex = sex;
 
  return result;
}

/* pretty print person p to standard output */
void prnt_person(person *p){
  printf("Name: %s\n"
         "Age: %i\n"
         "Sec: %c\n",
         p->name, p->age, p->sex);
}

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 person pointed to by p in the string str */
void encode_person(person *p, char *str){
  sprintf(str, "%s %i %c\n", 
          white_space_protect(p->name),
          p->age, p->sex);
} 

/* Decode the string str to a person and return it */
person *decode_person(char *str){
  char name[100];
  int age;
  char sex;

  sscanf(str, "%s %i %c", 
         name, &age, &sex);
    
  return make_person(white_space_deprotect(name), 
                     age, sex);
}


void print_person(person *p, FILE *ofp){
  char buffer[BUFFER_MAX];
  encode_person(p, buffer);
  fprintf(ofp, "%s", buffer);
}

person *read_person(FILE *ifp){
  char buffer[BUFFER_MAX];
  fgets(buffer, BUFFER_MAX, ifp);
  return decode_person(buffer);
}

I denne løsning har vi ikke frigivet dynamisk allokeret lager, og vi har ikke checket om malloc/ calloc kunne gennemføres. Det er naturligvis en svaghed.

Der følger ikke nogen main funktion med. Oversæt derfor ovenstående med -c (compile only) option.

Der er pt. ikke nogen løsning, der viser hvordan vi kan udskrive bitmønstret for struct person med fwrite.


13.4   Tynde matricer  

En vilkårlig matrix kan generelt repræsenteres på en tekstfil, med én linje pr. række. Første linje 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 vi 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 linje af tekstfilen indeholde dimensionerne af matricen - to heltal - antal rækker og antal søjler. Hver af de efterfølgende linjer indholder tre tal: Rækkenummer, søjlenummer, og et tal fra matricen (som ikke er nul). Om nødvendigt kan man gøre sig antagelser om rækkefølgen af linjer i den specielle tekstfil repræsentation.

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 er en tynd matrix

4 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

Løsning

Her er en mulig løsning på opgaven.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUF_MAX 100

void normal_matrix_to_sparse_matrix(char *matrix_in_file_name, 
                                    char *matrix_out_file_name){
  FILE *ifp, *ofp;
  char buf[BUF_MAX];
  int r, c, rows, cols;
  double value;

  // Open files:
  ifp = fopen(matrix_in_file_name, "r");
  ofp = fopen(matrix_out_file_name, "w");

  // Handle dimension. A line with two integers m n:
  fgets(buf, BUF_MAX, ifp);  
  fputs(buf, ofp);

  // Extract number of rows and number of colums to rows and cols:
  sscanf(buf, "%d %d", &rows, &cols);

  // Read normal matrix and print sparse matrix:
  for(r = 1; r <= rows; r++){
    for(c = 1; c <= cols; c++){ 
      fscanf(ifp, " %lf", &value);
      if (value != 0) fprintf(ofp, "%d %d %f\n", r, c, value);
    }
  }

  // Close files:
  fclose(ifp);
  fclose(ofp);
}

void sparse_matrix_to_normal_matrix(char *matrix_in_file_name,
                                    char *matrix_out_file_name){
  FILE *ifp, *ofp;
  char buf[BUF_MAX], *res;
  int rows, cols,
      row = -1, col = -1,   // Negative values signals no more 
      r, c;                 // entries in sparse representation.
  double value;

  // Open files:
  ifp = fopen(matrix_in_file_name, "r");
  ofp = fopen(matrix_out_file_name, "w");

  // Handle dimensions:
  fgets(buf, BUF_MAX, ifp);  
  fputs(buf, ofp);

  // Extract number of rows and number of colums:
  sscanf(buf, "%d %d", &rows, &cols);

  // Get first entry. Assign row, col and value.
  res = fgets(buf, BUF_MAX, ifp);
  if (res != NULL)
     sscanf(buf, "%d %d %lf", &row, &col, &value);
  else {row = -1; col = -1;}

  // Generate all entries, in a double for loop.
  // Read from sparse matrix along the way:
  for (r = 1; r <= rows; r++){
    for (c = 1; c <= cols; c++){
      if (r == row && c == col){
        fprintf(ofp, "%f ", value);

        // Get next entry: 
        if (res != NULL) res = fgets(buf, BUF_MAX, ifp);
        if (res != NULL)
           sscanf(buf, "%d %d %lf", &row, &col, &value);
        else {row = -1; col = -1;}
      }
      else fprintf(ofp, "%f ", 0.0);
   }
   fprintf(ofp, "\n");
  }

  // Close files:
  fclose(ifp);
  fclose(ofp);
}

int main(void) {
  normal_matrix_to_sparse_matrix("m1.dat", "ms.dat");
  sparse_matrix_to_normal_matrix("ms.dat", "m2.dat");
}


13.5   Læsning af personkartotek i komma-separeret tekstfil  

Skriv et program der indlæser tekstfilen i et array af følgende struct:

  #define MAX_NAME_LGT 50
  struct person{
    char fornavn[MAX_NAME_LGT];
    char efternavn[MAX_NAME_LGT];
    char vejnavn[MAX_NAME_LGT];
    int vejnummer;
    int postnummer;
    char bynavn[MAX_NAME_LGT];
  };

Indholdet af tekstfilen findes også nederst i opgaven.

Tekstfilen er linje-orienteret. Data om én person findes på én linje. Navne-delen, gade-delen og by-delen er adskilt af kommaer, og linjen er afsluttet med et punktum. Der optræder ikke cifre i fornavn efternavne, gadenavne eller bynavne. Hver person har ét fornavn og ét efternavn. Gadenavnet kan bestå af flere ord. Bynavnet består af netop et ord.

Overvej omhyggeligt hvordan du læser bestanddelene af en linje. Det er måske nyttigt for dig at bruge scansets i din løsning, som et alternativ til %s . Vær forsigtig med at ikke at få indlæst et komma som en del af efternavnet. Vær også forsigtig med, at et vejnavn ikke ender med et 'space'.

Sorter de indlæste data efter efternavn og udskriv de sorterede data på en anden fil. Filen skal have følgende simple linje-format:

  By: Efternavn

Denne fil skal genereres med det givne input.

 

Her er input filen til programmet:

Lars Jensen, Engtoften 23, 7182 Bredsten.
Bo Olsen, Gammel Vestergade 56, 4261 Dalmose.
Kurt Jensen, Haderslevvej 15, 8370 Hadsten.
Birte Madsen, Universitetsvej 899, 9000 Aalborg.
Kaj Moberg, Halevindingevej 2, 2670 Greve.
Bo Rise, Hadsund Landvej Nord 56, 8900 Randers.
Ebbe Rise, Hadsund Landvej Syd 58, 9520 Hobro.
Kalle Rastrup, Under Broen 567, 2650 Hvidovre.
Emil Rask, Over Broen 765, 2650 Glostrup.
Jens Larsen, Finkevej 1, 6682 Hovborg.
Lars Olsen, Gammel Vindingevej 2, 7560 Hjerm.

Her er output filen fra programmet:

Hadsten: Jensen
Bredsten: Jensen
Hovborg: Larsen
Aalborg: Madsen
Greve: Moberg
Hjerm: Olsen
Dalmose: Olsen
Glostrup: Rask
Hvidovre: Rastrup
Randers: Rise
Hobro: Rise

Løsning

Her er mit program:

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

#define MAX_NAME_LGT 50
#define MAX_PERSONS 25

struct person{
  char fornavn[MAX_NAME_LGT];
  char efternavn[MAX_NAME_LGT];
  char vejnavn[MAX_NAME_LGT];
  int vejnummer;
  int postnummer;
  char bynavn[MAX_NAME_LGT];
};

typedef struct person person;

person read_person(FILE *fp);
int read_all(char *file_name, person p_array[]);
void print_all(person all[], int n,  char* file_name);
void print_person(person p, FILE *fp);
void print_person_full(person p, FILE *fp);
int person_compare(const void *p1, const void *p2);
void trim_string(char *str);

int main(void) {
  person all_persons[MAX_PERSONS];
  int n;

  n = read_all("personer-ind.txt", all_persons);
  qsort(all_persons, n, sizeof(person), person_compare);
  print_all(all_persons, n, "personer-ud.txt");

  return 0;
}

person read_person(FILE *fp){
  person res;
  int scanres;
  char fn[MAX_NAME_LGT], en[MAX_NAME_LGT];

  scanres = fscanf(fp, " %s %[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ] ,"      /* Fornavn, efternavn uden komma, komma */
                       " %[^0123456789] %d ,"                                               /* Indtil vejnummer, vejnummer som int, saa komma */
                       " %d %[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ] . ",    /* Postnummer som int, bynavn uden punktum, punktum */
                   res.fornavn, res.efternavn,
                   res.vejnavn, &res.vejnummer,
                   &res.postnummer, res.bynavn);

  trim_string(res.vejnavn);

  if (scanres == 6){
     printf(".");
     return res;
  }
  else {
     printf("Input problems. Bye.\n");
     exit(EXIT_FAILURE);
  }
}

/* Eliminate trailing spaces from str */
void trim_string(char *str){
  int i = strlen(str) - 1;
  while (i >= 0 && str[i] == ' '){
     str[i] = '\0';
     --i;
  }
}

/* Read data in p_array, and return the number of persons read */
int read_all(char *file_name, person p_array[]){
  FILE *fp = fopen(file_name, "r");
  int i = 0;
  if (fp != NULL){
    while(!(feof(fp))){
       p_array[i] = read_person(fp);
       ++i;
    }
    fclose(fp);
    return i;
  }
  else {
    printf("Cannot open file %s\n", file_name);
    exit(EXIT_FAILURE);
  }
}

void print_all(person all[], int n, char* file_name){
  int i;
  FILE *fp = fopen(file_name, "w");
  if (fp != NULL){
    for (i = 0; i < n; ++i)
      print_person(all[i], fp);
    fclose(fp);
  }
  else {
    printf("Cannot open file %s\n", file_name);
    exit(EXIT_FAILURE);
  }
}

void print_person(person p, FILE *fp){
  fprintf(fp, "%s: %s\n", p.bynavn, p.efternavn);
}

void print_person_full(person p, FILE *fp){
  fprintf(fp, "%s %s, %s %d, %d %s\n", p.efternavn, p.bynavn, p.vejnavn, p.vejnummer, p.postnummer, p.bynavn);
}

int person_compare(const void *p1, const void *p2){
  return strcmp(((person*)p1)->efternavn, ((person*)p2)->efternavn);
}


Genereret: Torsdag 28. januar 2021, 10:50:26