简体   繁体   English

如何使用C中的数组中的元素查找和替换文本文件中的位置?

[英]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. 我有两个文本文件data.txt和template.txt。 I read and splitted the data in data.txt and stored them as an array. 我读取并拆分data.txt中的数据并将它们存储为数组。 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. 我的问题是将这些数据与template.txt文件一起使用,找到'$'符号并使用随后的数字作为customerData中数组的指示符。

data.txt 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 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? 我编写了代码但是我的大脑仍然坚持使用数组中的位置来获取template.txt。我需要帮助来解决如何在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;
}

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. 与每个客户相关联的信息集合的数据处理可以由包含每个字段的成员的struct来处理。 (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: (虽然每个字段的大小可以更加狭窄,但是30字段大小很好)例如,为FLDSZ = 30声明一个常量,您可以为您的信息创建一个类似于以下内容的结构:

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) (理想情况下你想动态分配指针的一些初始数目为结构,以填充和realloc需要。对于这个例子的目的,包含在阵列中的静态数量是罚款)

To begin storing your data, you need a way to break the line into the various tokens. 要开始存储数据,您需要一种方法将行划分为各种标记。 strtok is ideal here. strtok在这里很理想。 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) (其中ifp是您的输入文件流指针n您的struct索引)

The helper function fillsp is key. 帮助函数fillsp是关键。 It takes the address of struct sp , a pointer to the current token , and a pointer to the current member index idx . 它采用struct sp地址 ,指向当前标记的指针,以及指向当前成员索引 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. 根据idx的值,通过if-then-else字符串或更好的switch语句,您可以使用每个标记协调正确的成员。 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 ). 注意:当找到正确的大小写时会更新idx默认情况下会警告无效索引,但除了强制终止之外,还应该添加strlen检查)。

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. 存储数据后,下一个任务是简单地读取模板文件,并用结构中相应的字段替换'$X'格式占位符。 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. 需要注意的一点是,每次读取后都需要rewind 模板文件指针'tfp' ),以便再次用于下一个客户。 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) 注意:实际上,您将查找客户名称,然后将该地址传递给welcome函数,但是对于此示例,每个都将打印)

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. welcome函数中解析模板的每一行时, strchr函数提供了一种简单的方法来检查定位任何给定行中的任何'$' 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 . 要创建要输出的实际行,可以使用strcatstrncat For example, you could use something similar to the following for welcome : 例如,您可以使用类似于以下内容的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 '$' . welcome使用的最后一个辅助函数是rtnmemb用于返回'$'后面的字符标识的成员 ( 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: 注意:由于您将数字作为字符读取,因此需要从ASCII值中减去'0'以将其转换为数值。) rtnmemb的实现可能如下所示:

/* 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. 使用switch语句替换if-then-else语句的长链通常会带来好处。 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. 如果您对此方法有任何疑问,请与我们联系。

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 但+1是实现敏感表查找的唯一答案,而不是在交换机上浪费代码

If the problem had been stated to allow multichar field designators (eg $10 ), this would have been obvious to everyone. 如果已经说明了问题允许multichar现场指示符(例如$10 ),这对每个人来说都是显而易见的。 In the updated code, this is implemented in getfld . 在更新的代码中,这是在getfld实现的。 But, in the original, I just assumed it because it made the solution easier 但是,在原文中,我只是假设它,因为它使解决方案更容易

Are you sure about that if (chr >= fldcnt) ? 如果(chr >= fldcnt)你确定吗? Or maybe you're assuming there aren't any misplaced '$' characters, like $A , in the input. 或者也许你假设在输入中没有任何错误的'$'字符,比如$A

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 . 在原始版本中,只检查一个太大的字段编号,而不是检查格式错误的字段指示符,如$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. 对于生产代码,我们应该检查这个,就像我使用check_tmplcheck_cust等。 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 或者也许设置一下,所以$$打印一个$ ,允许货币,如$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 根据问题陈述,这有点过分,但[只是为了表明它可以完成]我已经在domoney添加了它

If the check fails, to print the $c literally if there's no replacement for it. 如果检查失败,如果没有替换,则按字面打印$c

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. 这就是我添加check_tmpl函数的原因。

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. 一个改进只是标记和跳过不良客户记录,但这取决于哪种公司“政策”将适用于这种情况。

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. 在解析数据并将其存储到customerData ,可以通过读取每个字符将数据叠加到数组索引上。

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

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++) . 如果您理解了上述内容,您可以在将来执行此操作:(i)将模板文件只读入一个长字符串(char [300])并重复使用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]) (ii)你可以理解'9' - '0'将导致int 0,'8' - '3'将导致int 5.所以你可以使用int col = next - '0'; printf("%s ", customerData[col]) int col = next - '0'; printf("%s ", customerData[col]) (which Barmer told) int col = next - '0'; printf("%s ", customerData[col]) (Barmer告诉)

good luck to you. 祝你好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM