我的目标是从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
。要使用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 ("%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
缓冲区无法保存整行。