我的sscanf如果陷入无限循环,该如何解决

时间:2018-10-22 00:35:54

标签: c while-loop char scanf fgets

我的目标是从test.txt中读取内容,然后输出其内容。但是,问题是,我陷入了sscanf循环中。因此,它会不断读取Australia

test.txt

    Australia   Sydney Perth Brisbane
    USA  California Los-Angeles Silicon-Valley Dallas
    Canada  Toronto

输出异常

Country: Australia
Cities: Sydney Perth Brisbane
---------------
Country: USA
Cities: California Los-Angeles Silicon-Valley Dallas
---------------
Country: Canada
Cities: Toronto
---------------

我的代码

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

#define MAX 2000

int main (void) {

   FILE *fp = fopen("test.txt" ,"r");
   char buf[MAX + 1];
   char country[MAX];
   char city[MAX];

   while (fgets(buf, MAX, fp) != NULL) {

      sscanf(buf, "%s", country);
      printf("Country: %s\n", country);

      printf("Cities:");
      while (sscanf(buf, "%s", city) == 1) {
         printf(" %s", city);
      }

      printf("\n---------------\n");
   }
}

3 个答案:

答案 0 :(得分:1)

您进入无限循环,因为您尝试从解析的city的同一位置(country的开头)解析每个buf。要使用sscanf逐步解析buf中的空格分隔的字符串,您需要另外使用"%n"转换说明符,以获取{消耗的字符数(下面的nchar)每次读取{1}}。然后,您可以将其添加到偏移量(下面的sscanf)中,以在解析off之后依次解析city中的每个buf

该方法很简单,将countrysscanf格式字符串一起使用,将空格分隔的字符串解析为一个数组,以节省"%s%n"在一个整数变量。例如:

sscanf

while (fgets (buf, MAXC, fp)) { /* read each line */ int nchar = 0; char cc[MAXC] = ""; /* buffer for country/city */ if (sscanf (buf, "%s%n", cc, &nchar)) { /* parse country, get used */ int off = nchar; /* add used char to offset */ printf ("%s\n", cc); /* read each city getting used chars to add to offset */ while (sscanf (buf + off, "%s%n", cc, &nchar) == 1) { printf (" %s\n", cc); off += nchar; } } } 上方的位置提供了buf + off中的位置,以开始解析每个城市。另请注意,使用buf不会增加转换计数(例如"%n"返回)。

完整示例:

sscanf

使用/输出示例

#include <stdio.h>

#define MAXC 2048   /* good use of constanst, but avoid common MAX */

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

    char buf[MAXC] = "";
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {             /* read each line */
        int nchar = 0;
        char cc[MAXC] = ""; /* buffer for country/city */
        if (sscanf (buf, "%s%n", cc, &nchar)) { /* parse country, get used */
            int off = nchar;                    /* add used char to offset */
            printf ("%s\n", cc);
            /* read each city getting used chars to add to offset */
            while (sscanf (buf + off, "%s%n", cc, &nchar) == 1) {
                printf ("  %s\n", cc);
                off += nchar;
            }
        }
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

虽然使用$ ./bin/rdcountrycity <dat/countrycity.txt Australia Sydney Perth Brisbane USA California Los-Angeles Silicon-Valley Dallas Canada Toronto 从每一行文本中分析国家和城市都不错,但是有一种工具更适合此工作,例如sscanf,用于根据您提供的定界符将字符串标记化为标记。您可以提供strtok(空格,制表符,换行符)的分隔符,以简单地解析每一行中每个由空格分隔的单词。

实际上要简单得多,例如

" \t\n"

(输出相同)

注意: #include <stdio.h> #include <string.h> #define MAXC 2048 /* good use of constanst, but avoid common MAX */ #define DELIM " \t\n" /* you can define character contstants too */ int main (int argc, char **argv) { char buf[MAXC] = ""; /* use filename provided as 1st argument (stdin by default) */ FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; if (!fp) { /* validate file open for reading */ perror ("file open failed"); return 1; } while (fgets (buf, MAXC, fp)) { /* read each line */ char *p = buf; if ((p = strtok (buf, DELIM))) { /* tokenize country */ printf ("%s\n", p); while ((p = strtok (NULL, DELIM))) /* tokenize each city */ printf (" %s\n", p); } } if (fp != stdin) fclose (fp); /* close file if not stdin */ return 0; } 修改原始字符串,因此如果需要,您需要复制strtok来保留原始字符串)

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

您需要有一个整数偏移量,将其命名为off,然后将buf + off传递为sscanf的第一个参数,而不只是buf

printf("Cities:");

int off = 0;
while (sscanf(buf + off, "%s", city) == 1) {
    printf(" %s", city);
    off += strlen(city);
}

for

int off;
for (off = 0; sscanf(buf + off, "%s", city) == 1; off += strlen(city))
    printf(" %s", city);

答案 2 :(得分:0)

您的代码有缓冲区溢出,这很危险。

从长行中读取时,line可能不会以\n结尾。如果该行不包含任何空格,则city缓冲区将无法容纳完整行。