c - 我的 sscanf 如果陷入无限循环,我该如何修复它

标签 c while-loop char scanf fgets

我的目标是阅读 test.txt ,然后输出其内容。然而,问题是,我陷入了 sscanf 循环。所以它一直显示Australia一遍又一遍

测试.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");
   }
}

最佳答案

您进入无限循环,因为您试图从解析 country 的同一位置解析每个 city - buf 的开头。要使用 sscanf 增量解析 buf 中的空格分隔字符串,您需要另外使用 "%n" 转换说明符来获取sscanf 在每次读取时消耗的字符(下面的 nchar)。然后,您可以将其添加到偏移量(下面的 off),以便在解析 country 后从 buf 连续解析每个 city >.

方法很简单,使用 sscanf"%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 修改原始字符串,因此您需要复制 buf如果需要保留原件)

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

关于c - 我的 sscanf 如果陷入无限循环,我该如何修复它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52921121/

相关文章:

c - 查找运行三个循环的代码的时间复杂度

c - 在C中如何允许&只接受两位数字作为输入?

java - 如何更改方法中字符串的字符? (相同字符,随机顺序)

c++ - 对字符数组感到困惑

c++ - String 到 char * 特殊字符的转换

C 程序编写不正确

c - OpenCV 中的仿射不变特征检测

mysql - 使用两个 MySQL 循环填充表

haskell - 使用 Haskell 循环遍历一组函数

java - 使用 while 循环打印值