Reputation: 644
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
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
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
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
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