Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I find and replace a position in a text file with element in an array in C?

Tags:

arrays

c

I have this two text files data.txt and template.txt. I read and splitted the data in data.txt and stored them as an array. My problem is using this data with the template.txt file, finding the '$' sign and using whatever number that follows as an indicator for the array in customerData.

data.txt

Public|Jane|Q|Ms.|600|Maple Street|Your Town|Iowa|12345
Penner|Fred|R|Mr.|123|that Street|Winnipeg|MB|R3T 2N2
Gardner|Mark|E|Mr.|76|The Avenue|Toronto|ON|M5W 1E6
Acton|Hilda|P|Mrs.|66|What Blvd|Winnipeg|MB|R3T 2N2

template.txt

Welcome back, $1!
We hope that you and all the members
of the $0 family are constantly
reminding your neighbours there
on $5 to shop with us.
As usual, we will ship your order to
   $3 $1 $2. $0
   $4 $5
   $6, $7 $8

The output should be something like this:

Welcome back, Jane!
We hope that you and all the members
of the Public family are constantly
reminding your neighbors there
on Maple Street to shop with us.
As usual, we will ship your order to
    Ms. Jane Q. Public
    600 Maple Street
    Your Town, Iowa 12345

I wrote the code but my brain got stuck at using the position in the array for template.txt.I need help on how to do this in C? Any contribution would be appreciated.

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

#define INPUT_LENGTH 128
#define FIELD_LENGTH 30
#define NUM_FIELDS   9

int main( int argc, char *argv[] )
{
  FILE *template = NULL;
  FILE *data = NULL;

  char input[INPUT_LENGTH];
  char customerData[NUM_FIELDS][FIELD_LENGTH];
  int  element = 0;
  char *next;
  char ch;

  template = fopen( "template.txt", "r" );
  if ( template != NULL )
  {
    // read in the customers until we're done
    data = fopen( "data.txt", "r" );
    if ( data != NULL )
    {

      while(fgets(input, INPUT_LENGTH,data) != NULL){
        next = strtok(input, "|"); //splitting the data stored in input by |
        while(next != NULL){
          strcpy(customerData[element],next);
          printf("%s\n", customerData[element] );  //prints the first element in the array
          next =strtok(NULL, "|");
          element++;
          while((ch=fgetc(template)) != NULL){
            if(ch == '0'){

            }else if(ch == '1'){

            }else if(ch == '2'){
                //do sth
            }else if(ch == '3'){
            }else if(ch == '4'){
            }else if(ch == '5'){
            }else if(ch == '6'){
            }else if(ch == '7'){
            }else if(ch == '8'){
            }else if(ch == '9'){
            }
          }
          /*while(fgets(input, INPUT_LENGTH,template) != NULL){
            for(int i=0; i< strlen(input); i++){
              ch= strchr(template, "$")

              ch = input[INPUT_LENGTH];
              if(ch != "$"){
                if(ch == "0"){
                  printf("%s\n", hi );

                }
              }
            }

          }*/
        }
      }






      fclose( data );
    }

    fclose( template );
  }

  return EXIT_SUCCESS;
}
like image 806
ekeith Avatar asked Sep 25 '22 13:09

ekeith


2 Answers

There are various ways to approach this problem. All of them can be made easier by breaking the problem down into several helper functions that will help keep the logic of the program straight. (note: that is true for any piece of code, but it is particularly true for this bit of code).

The data handling for the collection of information associated with each customer can be handled by a struct containing members for each field. (while each field can be more narrowly tailored in size, your fieldsize of 30 is fine) For example, declaring a constant for FLDSZ = 30, you can create a structure for your information similar to:

typedef struct {
    char last[FLDSZ];
    char first[FLDSZ];
    char mi[FLDSZ];
    char sal[FLDSZ];
    char street[FLDSZ];
    char streetno[FLDSZ];
    char city[FLDSZ];
    char state[FLDSZ];
    char zip[FLDSZ];
} sp;

(ideally you would want to dynamically allocate some initial number of pointers to struct, to fill and realloc as required. For purposes of this example, a static number contained in an array is fine)

To begin storing your data, you need a way to break the line into the various tokens. strtok is ideal here. You simple need to read each line, tokenize it, and store the resulting string as the correct member. With an array of structs, you also need to keep track of the individual struct index in addition to coding a way to store the individual tokens as the correct member. This is where the first helper function can make life a bit easier. For instance, your entire read/fill of the data structure can be done with something similar to:

char buf[MAXC] = {0};
sp s[MAXS];
....
while (fgets (buf, MAXC, ifp)) { /* read each line of data */
    char *p;
    size_t idx = 0; /* tokenize/save in struct 's[n]' */
    for (p = strtok (buf, "|"); p; p = strtok (NULL, "|\n")) {
        fillsp (&s[n], p, &idx);  /* assign to correct member */
    }
    if (++n == MAXS) { /* limit reached */
        fprintf (stderr, "MAXS structs filled.\n");
        break;
    }
}

(where ifp is your input file stream pointer and n your struct index)

The helper function fillsp is key. It takes the address of struct sp, a pointer to the current token, and a pointer to the current member index idx. Based on the value of idx, through either a string of if-then-else, or better, a switch statement you can coordinate the correct member with each token. Something similar to the following fits here:

/* store 'p' in correct stuct 's' member based on index 'idx' */
void fillsp (sp *s, const char *p, size_t *idx)
{
    switch (*idx) {
        case 0 :
            strncpy (s->last, p, FLDSZ);
            if (s->last[FLDSZ - 1] != 0) s->last[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 1 :
            strncpy (s->first, p, FLDSZ);
            if (s->first[FLDSZ - 1] != 0) s->first[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 2 :
            s->mi[0] = s->mi[1] = 0;
            *(s->mi) = *p;
            (*idx)++;
            break;
        case 3 :
            strncpy (s->sal, p, FLDSZ);
            if (s->sal[FLDSZ - 1] != 0) s->sal[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 4 :
            strncpy (s->streetno, p, FLDSZ);
            if (s->streetno[FLDSZ - 1] != 0) s->streetno[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 5 :
            strncpy (s->street, p, FLDSZ);
            if (s->street[FLDSZ - 1] != 0) s->street[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 6 :
            strncpy (s->city, p, FLDSZ);
            if (s->city[FLDSZ - 1] != 0) s->city[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 7 :
            strncpy (s->state, p, FLDSZ);
            if (s->state[FLDSZ - 1] != 0) s->state[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 8 :
            strncpy (s->zip, p, FLDSZ);
            if (s->zip[FLDSZ - 1] != 0) s->zip[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        default :
            fprintf (stderr, "error: index outside allowed 0-8.\n");
            exit (EXIT_FAILURE);
    }
}

(note: idx is updated when a correct case is found. The default case warns of an invalid index, but you should also add strlen checks in addition to forcing nul-termination).

After storing your data, the next task is to simply read the template file and substitute the '$X' format placeholder with the appropriate field from your struct. One thing to be aware of is that you will need to rewind the template-file-pointer ('tfp') after each read to allow it to be used again for the next customer. Here again, a helper function helps. The logic for displaying the welcome information for each customer can be a simple as:

for (i = 0; i < n; i++)     /* show welcome for each */
    welcome (&s[i], tfp);

(note: in actuality, you would do a lookup for the customer name, and then just pass that address to the welcome function, but for this example each will be printed)

When parsing each line of the template in the welcome function, the strchr function provides an easy way to both check for, and locate any '$' within any given line. By using a couple of character pointers, you can easily loop through each line and locate/substitute for each format specifier. To create the actual line to be output, you can use strcat and strncat. For example, you could use something similar to the following for welcome:

void welcome (sp *s, FILE *tfp)
{
    char buf[MAXC] = {0};
    while (fgets (buf, MAXC, tfp)) {
        char *p = buf, *ep;
        char obuf[MAXC] = {0};
        while (*p && (ep = strchr (p, '$'))) {
            strncat (obuf, p, ep++ - p);
            strcat  (obuf, rtnmemb (s, *ep++ - '0'));
            p = ep;
        }
        strcat (obuf, p);
        printf ("%s", obuf); /* relies on trailing '\n' from read */
    }
    putchar ('\n');
    rewind (tfp);
}

The last helper function used by welcome is rtnmemb used to return the member identified by the character following '$'. (note: since you read the number as a character, you need to subtract '0' from the ASCII value to covert it to a numeric value.) An implementation of rtnmemb could look like:

/* return correct member of struct 's' based on 'idx' */
char *rtnmemb (sp *s, size_t idx)
{
    switch (idx) {
        case 0 : return s->last; break;
        case 1 : return s->first; break;
        case 2 : return s->mi; break;
        case 3 : return s->sal; break;
        case 4 : return s->streetno; break;
        case 5 : return s->street; break;
        case 6 : return s->city; break;
        case 7 : return s->state; break;
        case 8 : return s->zip; break;
        default : printf ("error: requested member outside allowed 0-8.\n");
    }
    return NULL;
}

Putting all the pieces of the puzzle together, you could do something like:

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

/* constants (file size, max struct, max char) */
enum { FLDSZ = 30, MAXS = 64, MAXC = 128 };

typedef struct {
    char last[FLDSZ];
    char first[FLDSZ];
    char mi[FLDSZ];
    char sal[FLDSZ];
    char street[FLDSZ];
    char streetno[FLDSZ];
    char city[FLDSZ];
    char state[FLDSZ];
    char zip[FLDSZ];
} sp;

void fillsp (sp *s, const char *p, size_t *idx);
char *rtnmemb (sp *s, size_t idx);
void welcome (sp *s, FILE *tfp);

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

    char buf[MAXC] = {0};
    sp s[MAXS];
    size_t i, n = 0;  /* input, template, output streams */
    FILE *ifp = argc > 1 ? fopen (argv[1], "r") : stdin;
    FILE *tfp = fopen (argc > 2 ? argv[2] : "../dat/template.txt", "r");
    FILE *ofp = argc > 3 ? fopen (argv[3], "w") : stdout;

    if (!ifp || !tfp || !ofp) { /* validate streams open */
        fprintf (stderr, "error: file open failed.\n");
        return 1;
    }

    while (fgets (buf, MAXC, ifp)) { /* read each line of data */
        char *p;
        size_t idx = 0; /* tokenize/save in struct 's[n]' */
        for (p = strtok (buf, "|"); p; p = strtok (NULL, "|\n")) {
            fillsp (&s[n], p, &idx);
        }
        if (++n == MAXS) { /* limit reached */
            fprintf (stderr, "MAXS structs filled.\n");
            break;
        }
    }

    for (i = 0; i < n; i++)     /* show welcome for each */
        welcome (&s[i], tfp);

    if (ifp != stdin)  fclose (ifp);    /* close files */
    if (ofp != stdout) fclose (ofp);
    fclose (tfp);

    return 0;
}

/* store 'p' in correct stuct 's' member based on index 'idx' */
void fillsp (sp *s, const char *p, size_t *idx)
{
    switch (*idx) {
        case 0 :
            strncpy (s->last, p, FLDSZ);
            if (s->last[FLDSZ - 1] != 0) s->last[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 1 :
            strncpy (s->first, p, FLDSZ);
            if (s->first[FLDSZ - 1] != 0) s->first[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 2 :
            s->mi[0] = s->mi[1] = 0;
            *(s->mi) = *p;
            (*idx)++;
            break;
        case 3 :
            strncpy (s->sal, p, FLDSZ);
            if (s->sal[FLDSZ - 1] != 0) s->sal[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 4 :
            strncpy (s->streetno, p, FLDSZ);
            if (s->streetno[FLDSZ - 1] != 0) s->streetno[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 5 :
            strncpy (s->street, p, FLDSZ);
            if (s->street[FLDSZ - 1] != 0) s->street[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 6 :
            strncpy (s->city, p, FLDSZ);
            if (s->city[FLDSZ - 1] != 0) s->city[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 7 :
            strncpy (s->state, p, FLDSZ);
            if (s->state[FLDSZ - 1] != 0) s->state[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        case 8 :
            strncpy (s->zip, p, FLDSZ);
            if (s->zip[FLDSZ - 1] != 0) s->zip[FLDSZ - 1] = 0;
            (*idx)++;
            break;
        default :
            fprintf (stderr, "error: index outside allowed 0-8.\n");
            exit (EXIT_FAILURE);
    }
}

/* return correct member of struct 's' based on 'idx' */
char *rtnmemb (sp *s, size_t idx)
{
    switch (idx) {
        case 0 : return s->last; break;
        case 1 : return s->first; break;
        case 2 : return s->mi; break;
        case 3 : return s->sal; break;
        case 4 : return s->streetno; break;
        case 5 : return s->street; break;
        case 6 : return s->city; break;
        case 7 : return s->state; break;
        case 8 : return s->zip; break;
        default : printf ("error: requested member outside allowed 0-8.\n");
    }
    return NULL;
}

void welcome (sp *s, FILE *tfp)
{
    char buf[MAXC] = {0};
    while (fgets (buf, MAXC, tfp)) {
        char *p = buf, *ep;
        char obuf[MAXC] = {0};
        while (*p && (ep = strchr (p, '$'))) {
            strncat (obuf, p, ep++ - p);
            strcat  (obuf, rtnmemb (s, *ep++ - '0'));
            p = ep;
        }
        strcat (obuf, p);
        printf ("%s", obuf);
    }
    putchar ('\n');
    rewind (tfp);
}

Output

$ ./bin/str_template ../dat/data.txt
Welcome back, Jane!
We hope that you and all the members
of the Public family are constantly
reminding your neighbours there
on Maple Street to shop with us.
As usual, we will ship your order to
   Ms. Jane Q. Public
   600 Maple Street
   Your Town, Iowa 12345

Welcome back, Fred!
We hope that you and all the members
of the Penner family are constantly
reminding your neighbours there
on that Street to shop with us.
As usual, we will ship your order to
   Mr. Fred R. Penner
   123 that Street
   Winnipeg, MB R3T 2N2

Welcome back, Mark!
We hope that you and all the members
of the Gardner family are constantly
reminding your neighbours there
on The Avenue to shop with us.
As usual, we will ship your order to
   Mr. Mark E. Gardner
   76 The Avenue
   Toronto, ON M5W 1E6

Welcome back, Hilda!
We hope that you and all the members
of the Acton family are constantly
reminding your neighbours there
on What Blvd to shop with us.
As usual, we will ship your order to
   Mrs. Hilda P. Acton
   66 What Blvd
   Winnipeg, MB R3T 2N2

Look over the approach to the problem. There is often a benefit to replacing a long chain of if-then-else statements with a switch statement. Both are acceptable, and that goes to any of the different approaches to your problem. As long as any approach handles the data correctly, is readable and reasonably efficient and provides the correct output, it is ultimately a matter of taste. Let me know if you have any questions about the approach.

like image 65
David C. Rankin Avatar answered Oct 11 '22 14:10

David C. Rankin


I know this post is old but i ran into the same problem and i am going to share my code for future readers. This is how i got it done. I did not have to do anything complicated. After parsing the data and storing it into customerData, something like this can be done to superimpose the data onto the array indices by reading each character.

    rewind( template );
    while ( fgets( input, INPUT_LENGTH, template ) != NULL )
    {
      // Process the input one character at a time looking for variables to replace with customerData
      element = 0;
      ch = input[element];

      while(ch) {
        if(ch != '$') {
          printf("%c", ch);
        } else {
          element++;
          ch = input[element];
          printf("%s", customerData[atoi( &ch)]);
        }
        element++;
        ch = input[element];
      }
     }
     printf("\n");
like image 24
Saad Avatar answered Oct 11 '22 13:10

Saad