Index over opgaver i denne lektion   Alfabetisk indeks   Kursets hjemmeside   

Opgaver og løsninger
Input/Output og Filer


11.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 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).

Løsning

Her er min løsning:

/* Exercise 13-15, CBD, page 467. Programmed by Kurt Normark, 
   April 2003. */

#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;
}


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

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

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

  char line[MAXLINE], *pattern, *file_name;
  FILE *ifp;
  int line_numbers, n = 1;

  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);
  }
  
  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 (line_numbers)
        printf("%3i: %s", n, line);
      else
        printf("%s", line);
    }
    n++;
  }

  fclose(ifp);
  
  return 0;
}

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 pattern simple-grep.c

Outputtet er:

   char line[MAXLINE], *pattern, *file_name;
   file_name = argv[2]; line_numbers = 0; pattern = argv[1];
   file_name = argv[3]; line_numbers = 1; pattern = argv[2];
   if (strstr(line, pattern) != NULL){

Hele problemløsningen forekommer i main funktionen, hvilket er dårlig stil.


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

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.

Løsning

Her er en mulig løsning på opgaven:

#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);
}

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


11.4   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

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


Genereret: Tirsdag 13. november 2012, 16:02:22