ekeith
ekeith

Reputation: 644

How do I find and replace a position in a text file with element in an array in 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;
}

Upvotes: 5

Views: 373

Answers (4)

Saad
Saad

Reputation: 397

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

Upvotes: 1

Craig Estey
Craig Estey

Reputation: 33601

I think this can be done in a simpler way:

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

#define FLDMAX          30

// docust -- process single customer line
void
docust(FILE *ftmpl,char *buf)
{
    int fldcnt;
    char *bp;
    char *cp;
    int chr;
    int prev;
    char *fields[FLDMAX];

    fputc('\n',stdout);

    // split customer line into fields
    fldcnt = 0;
    bp = buf;
    while (1) {
        cp = strtok(bp,"|");
        bp = NULL;

        if (cp == NULL)
            break;

        fields[fldcnt++] = cp;

        if (fldcnt >= FLDMAX)
            break;
    }

    rewind(ftmpl);

    // output the form letter
    prev = EOF;
    while (1) {
        chr = fgetc(ftmpl);
        if (chr == EOF)
            break;

        prev = chr;

        // output ordinary char
        if (chr != '$') {
            fputc(chr,stdout);
            continue;
        }

        // get field designator (e.g. $3)
        chr = fgetc(ftmpl);

        // point to correct customer field
        chr -= '0';
        if (chr >= fldcnt)
            continue;
        cp = fields[chr];

        fputs(cp,stdout);
    }

    // malformed template file (e.g. has no newline at end)
    if (prev != '\n')
        fputc('\n',stdout);
}

int
main(int argc,char **argv)
{
    FILE *ftmpl;
    FILE *fcust;
    char *cp;
    char buf[5000];

    fcust = fopen("data.txt","r");
    ftmpl = fopen("template.txt","r");

    while (1) {
        cp = fgets(buf,sizeof(buf),fcust);
        if (cp == NULL)
            break;

        cp = strchr(buf,'\n');
        if (cp != NULL)
            *cp = 0;

        docust(ftmpl,buf);
    }

    fclose(fcust);
    fclose(ftmpl);

    return 0;
}

UPDATE:

Here is a version that is closer to what I would do for a "production grade" app. It's mostly based on Peter's comments below

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

typedef unsigned int u32;

// check for value within a given range
#define RANGE(_val,_lo,_hi) \
    (((_val) >= (_lo)) && ((_val) <= (_hi)))

#define FLDMAX          30              // maximum number of fields

// all options
#define OPTALL(_cmd) \
    _cmd(ERRSPLIT,0,"too many fields in customer record") \
    _cmd(ERRSYN,1,"malformed $x in template") \
    _cmd(ERRFIELD,2,"template $x too large") \
    _cmd(ERRNL,3,"template missing final newline") \
    _cmd(BADMONEY,4,"malformed money amount")

// define option symbols
#define _OPTDEF(_sym,_val,_reason) \
    OPT_##_sym = 1u << ((_val) + 16),
enum {
    OPTALL(_OPTDEF)
};

#define OPTMSK      0xFFFF0000
#define OPTVAL      0x0000FFFF

// option control
struct opt {
    u32 opt_val;                        // option value
    const char *opt_tag;                // option name
    const char *opt_reason;             // option explanation
};

// option table
#define _OPTGEN(_sym,_val,_reason) \
    { .opt_val = OPT_##_sym, .opt_tag = #_sym, .opt_reason = _reason },
struct opt opt_table[] = {
    OPTALL(_OPTGEN)
    { . opt_tag = NULL }
};

// abort
#define sysfault(_fmt...) \
    do { \
        fprintf(stderr,_fmt); \
        exit(1); \
    } while (0)

// xfopen -- open file
FILE *
xfopen(const char *file,const char *mode)
{
    FILE *xf;

    xf = fopen(file,mode);
    if (xf == NULL)
        sysfault("xfopen: unable to open '%s' -- %s\n",file,strerror(errno));

    return xf;
}

// xfclose -- close file
FILE *
xfclose(FILE *xf)
{

    if (xf != NULL) {
        fclose(xf);
        xf = NULL;
    }

    return xf;
}

// showerr -- show errors
void
showerr(u32 err)
{
    struct opt *opt;

    err &= OPTMSK;

    if (err != 0) {
        for (opt = opt_table;  opt->opt_tag != NULL;  ++opt) {
            if (err & opt->opt_val)
                fprintf(stderr,"showerr: %s -- %s\n",
                    opt->opt_tag,opt->opt_reason);
        }
        sysfault("showerr: aborting ...\n");
    }
}

// getfld -- get field designator
// RETURNS: field number (-1 means malformed (e.g. $X))
int
getfld(FILE *ftmpl)
{
    int chr;
    int acc;

    // assume malformed
    acc = -1;

    while (1) {
        chr = fgetc(ftmpl);
        if (chr == EOF)
            break;

        if (! RANGE(chr,'0','9')) {
            ungetc(chr,ftmpl);
            break;
        }

        if (acc < 0)
            acc = 0;
        acc *= 10;

        chr -= '0';
        acc += chr;
    }

    return acc;
}

// domoney -- output a monetary amount
// RETURNS: error mask
u32
domoney(FILE *fout,FILE *ftmpl)
{
    int chr;
    int cents;
    u32 opt;

    opt = 0;

    fputc('$',fout);

    // get dollars
    while (1) {
        chr = fgetc(ftmpl);

        if (chr == EOF) {
            opt |= OPT_BADMONEY;
            break;
        }

        fputc(chr,fout);

        if (chr == '.')
            break;

        // got something like "$$23x"
        if (! RANGE(chr,'0','9')) {
            opt |= OPT_BADMONEY;
            break;
        }
    }

    // get cents
    for (cents = 1;  cents <= 2;  ++cents) {
        if (opt != 0)
            break;

        chr = fgetc(ftmpl);

        // got something like "$$23."
        if (chr == EOF) {
            opt |= OPT_BADMONEY;
            break;
        }

        fputc(chr,fout);

        // got something like "$$23.x"
        if (! RANGE(chr,'0','9')) {
            opt |= OPT_BADMONEY;
            break;
        }
    }

    return opt;
}

// dosplit -- split customer line into fields
// RETURNS: number of fields (-1=overflow)
int
dosplit(char **fields,char *buf)
{
    int fldcnt;
    char *bp;
    char *cp;

    fldcnt = 0;
    bp = buf;

    while (1) {
        cp = strtok(bp,"|");
        bp = NULL;

        if (cp == NULL)
            break;

        fields[fldcnt++] = cp;

        if (fldcnt > FLDMAX) {
            fldcnt = -1;
            break;
        }
    }

    return fldcnt;
}

// docust -- process single customer line
// RETURNS: options
u32
docust(FILE *fout,FILE *ftmpl,char *buf)
{
    int chr;
    int prev;
    int fldcnt;
    int fldidx;
    int fldused;
    char *cp;
    char *fields[FLDMAX];
    u32 opt;

    opt = 0;
    fldidx = 0;
    fldused = -1;

    // split customer line into fields
    fldcnt = dosplit(fields,buf);
    if (fldcnt < 0)
        opt |= OPT_ERRSPLIT;

    rewind(ftmpl);

    fputc('\n',fout);

    // output the form letter
    prev = EOF;
    while (1) {
        chr = fgetc(ftmpl);
        if (chr == EOF)
            break;

        prev = chr;

        // output ordinary char
        if (chr != '$') {
            fputc(chr,fout);
            continue;
        }

        // check for '$$' for literal '$' for money designator
        // NOTE: this is vast overkill, based on the problem description
        chr = fgetc(ftmpl);
        if (chr == '$') {
            opt |= domoney(fout,ftmpl);
            continue;
        }
        ungetc(chr,ftmpl);

        // get field designator (e.g. $3)
        fldidx = getfld(ftmpl);

        // malformed designator (e.g. $X)
        if (fldidx < 0) {
            opt |= OPT_ERRSYN;
            continue;
        }

        // point to correct customer field
        if (fldidx >= fldcnt) {
            opt |= OPT_ERRFIELD;
            continue;
        }
        cp = fields[fldidx];

        // remember the largest field index we actually use
        if (fldidx > fldused)
            fldused = fldidx;

        fputs(cp,fout);
    }

    // malformed template file (e.g. has no newline at end)
    // technically an error but we can handle it
    if (prev != '\n') {
        fputc('\n',fout);
        opt |= OPT_ERRNL;
    }

    opt |= fldused;

    return opt;
}

// check_tmpl -- validate form letter template file
// RETURNS: the maximum field index used by the template file
int
check_tmpl(FILE *fout,FILE *ftmpl)
{
    int fldidx;
    char *bp;
    u32 err;
    char buf[5000];

    bp = buf;
    for (fldidx = 0;  fldidx < FLDMAX;  ++fldidx)
        bp += sprintf(bp,"|fld%d",fldidx);

    err = docust(fout,ftmpl,buf + 1);
    showerr(err);

    // the maximum index we actually used
    fldidx = err & OPTVAL;

    return fldidx;
}

// check_cust -- validate customer entries
void
check_cust(FILE *fout,FILE *fcust,int fldused)
{
    int fldcnt;
    u32 err;
    char *cp;
    char buf[5000];
    char *fields[FLDMAX];

    rewind(fcust);

    err = 0;

    while (1) {
        cp = fgets(buf,sizeof(buf),fcust);
        if (cp == NULL)
            break;

        cp = strchr(buf,'\n');
        if (cp != NULL)
            *cp = 0;

        fldcnt = dosplit(fields,buf);
        if (fldcnt < 0)
            err |= OPT_ERRSPLIT;

        if (fldcnt != (fldused + 1))
            err |= OPT_ERRFIELD;

        showerr(err);
    }
}

// main -- main program
int
main(int argc,char **argv)
{
    FILE *ftmpl;
    FILE *fcust;
    FILE *fout;
    int fldused;
    char *cp;
    char buf[5000];

    fcust = xfopen("data.txt","r");
    ftmpl = xfopen("template.txt","r");

    // pre-validate input files
    fout = xfopen("/dev/null","w");
    fldused = check_tmpl(fout,ftmpl);
    check_cust(fout,fcust,fldused);
    fout = xfclose(fout);

    rewind(fcust);
    while (1) {
        cp = fgets(buf,sizeof(buf),fcust);
        if (cp == NULL)
            break;

        cp = strchr(buf,'\n');
        if (cp != NULL)
            *cp = 0;

        docust(stdout,ftmpl,buf);
    }

    fcust = xfclose(fcust);
    ftmpl = xfclose(ftmpl);

    return 0;
}

Peter's comments:

But +1 for being the only answer to implement the sensible table-lookup instead of wasting code on a switch

If the problem had been stated to allow multichar field designators (e.g. $10), this would have been obvious to everyone. In the updated code, this is implemented in getfld. But, in the original, I just assumed it because it made the solution easier

Are you sure about that if (chr >= fldcnt)? Or maybe you're assuming there aren't any misplaced '$' characters, like $A, in the input.

Yes. In the original, it was only to check for a field number that was too large and not to check for a malformed field designator such as $X.

Based on the simple problem statement, we can assume the template is well formed. For production code, we should check for this as I did with check_tmpl, check_cust et. al. But, notice how much extra code is required to really do a thorough check [and it can be made even more thorough].

Or maybe set things up so $$ prints a single $, to allow currency like $15.25

Fair enough. It's a bit of overkill based on the problem statement, but [just to show that it can be done] I've added it in domoney

If the check fails, to print the $c literally if there's no replacement for it.

No. If we spot a malformed designator, we want that to be a hard error. If the template file is malformed, we want to abort rather than printing millions of form letters with mistakes. That's why I added the check_tmpl function.

The cost of paper and postage [or cost of email or instant message] for sending out bad letters can be huge. Not to mention the damage to a company's reputation.

Because the customer file is like a database, we can and should perform rigorous checks because, presumably, the database input has already been prechecked.

The program will abort if any customer record is malformed. A refinement would be to merely flag and skip bad customer records, but that depends upon what company "policy" would be in place for such situations.

Upvotes: 1

David C. Rankin
David C. Rankin

Reputation: 84559

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.

Upvotes: 3

Jay Kumar R
Jay Kumar R

Reputation: 537

Ok.. you should do something similar to this:

 element++;
 } /* finish processing ONE line and gather all array
 /*  open the file now. not before */
 template = fopen( "template.txt", "r" );
 while((ch=fgetc(template)) != NULL){
      if ( ch == '$' ) {
         char next=fgetc(template));
         if ( next == '1' ) {
              printf("%s ",customerData[1]);
         ...

      } else { 
        printf("%c",ch); /* just print as it is */
      }
 printf("\n");
 fclose(template);
/*process next line in outer while loop */

If you understood the above, you can in the future, do this : (i) read the template file just once into a long string ( char[300] ) and reuse it with a for (i=0;i<strlen(template_string);i++) .

(ii) You can understand that '9' - '0' will result in int 0, '8' - '3' will result in int 5. So you can use int col = next - '0'; printf("%s ", customerData[col]) (which Barmer told)

good luck to you.

Upvotes: -1

Related Questions