C语言 我的 sscanf 如果卡在无限循环中,我该如何修复它



我的目标是从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: %sn", country);
printf("Cities:");
while (sscanf(buf, "%s", city) == 1) {
printf(" %s", city);
}
printf("n---------------n");
}
}

您进入了一个无限循环,因为您尝试从您解析country的同一位置(buf的开头(解析每个city。要使用sscanfbuf中增量解析空格分隔的字符串,您需要另外使用"%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 ("%sn", cc);
/* read each city getting used chars to add to offset */
while (sscanf (buf + off, "%s%n", cc, &nchar) == 1) {
printf ("  %sn", 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 ("%sn", cc);
/* read each city getting used chars to add to offset */
while (sscanf (buf + off, "%s%n", cc, &nchar) == 1) {
printf ("  %sn", 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用于根据您提供的分隔符将刺痛标记为令牌。您可以提供" tn"分隔符(空格、制表符、换行符(,以简单地解析每行中的每个空格分隔词。

它实际上要简单得多,例如

#include <stdio.h>
#include <string.h>
#define MAXC 2048       /* good use of constanst, but avoid common MAX */
#define DELIM " tn"   /* 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 ("%sn", p);
while ((p = strtok (NULL, DELIM)))  /* tokenize each city */
printf ("  %sn", p);
}
}
if (fp != stdin) fclose (fp);   /* close file if not stdin */
return 0;
}

(输出相同(

(注意:strtok修改原始字符串,因此如果需要,您需要复制buf以保留原始字符串(

仔细看看,如果您有任何其他问题,请告诉我。

你需要有一个整数偏移量,称之为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);

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

当您从一长行中阅读时,line可能不会以n结尾。如果该行不包含任何空格,则city缓冲区无法保存整行。

相关内容

  • 没有找到相关文章

最新更新