问题描述
1 2 3
5 7 10 12
8 9 14 13 15 17
我将使用C读取三行之一,并返回元素的数量。
例如,我想将第二行5 7 10 12
读入内存,并返回第二行中的值数4
。我的代码在下面...
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat","r");
char line[STRING_SIZE];
int lcount = 0,nline = 1,sum = 0,number;
if (fp != NULL) {
while (fgets(line,STRING_SIZE,fp) != NULL) {
if (lcount == nline) {
while (sscanf(line,"%d ",&number)) {
sum++;
}
break;
} else {
lcount++;
}
}
fclose(fp);
}
exit(0);
}
运行此代码时,它永远不会像死循环一样停止。这是什么问题?
解决方法
循环while (sscanf(line,"%d ",&number))
继续解析该行中的第一个数字。
您应该改用strtol
:
#include <stdio.h>
#include <stdlib.h>
#define STRING_SIZE 2000
int main() {
FILE *fp = fopen("in.dat","r");
char line[STRING_SIZE];
int lcount = 0,nline = 1;
if (fp != NULL) {
while (fgets(line,STRING_SIZE,fp) != NULL) {
if (lcount == nline) {
char *p = line,*q;
int count = 0;
for (;;) {
long val = strtol(p,&q,0); // parse an integer
if (q == p) {
// end of string or not a number
break;
}
// value was read into val. You can use it for whatever purpose
count++;
p = q;
}
printf("%d\n",count);
break;
} else {
lcount++;
}
}
fclose(fp);
}
return 0;
}
,
chqrlie 答案的较干净版本。以字符串开头,这就是fgets()
之后的问题所在。
sscanf()
不会单步执行字符串,而是始终从头开始读取。
strtol()
在字符串的开头查找long int
,忽略初始空格。返回停止扫描的地址。
strtol()
的手册中说,应该检查errno是否存在任何转换错误。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define STRING_SIZE 2000
int main(void)
{
char line[STRING_SIZE] = "5 7 10 12";
char* start = line;
char* end;
int count = 0;
while(1)
{
/**
* strtol() look for long int in beginning of the string
* Ignores beginning whitespace
*
* start: where to strtol() start looking for long int
* end: where strtol() stops scanning for long int
*/
errno = 0; // As strol() manual says
strtol(start,&end,0);
if (errno != 0)
{
printf("Error in strtol() conversion.\n");
exit(0);
}
if (start == end) break; // Quit loop
start = end;
count++;
}
printf("count: %d\n",count);
return 0;
}
,
您正在使用sscanf()
来思考正确的方法,唯一缺少的难题是如何对line
应用偏移量,以便您读取{与您下一次致电sscanf()
的通话保持一致。您可以通过使用sscanf()
转换跟踪每次调用"%n"
所消耗的字符数来完成此操作(它不会添加到sscanf()
返回的转换计数中),例如读取行从打开的文件流fp
中,您可以执行以下操作:
#define MAXC 1024 /* if you need a constant,#define one (or more) */
...
char line[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (line,MAXC,fp)) { /* reach each line in file */
int offset = 0,/* offset in line for next sscanf() read */
nchr = 0,/* number of char consumed by last read */
val,/* integer value read with sscanf() */
nval = 0; /* number of values read in line */
/* conververt each integer at line + offset,saving no. of chars consumed */
while (sscanf (line + offset,"%d%n",&val,&nchr) == 1) {
printf (" %d",val); /* output value read */
offset += nchr; /* update offset with no. chars consumend */
nval++; /* increment value count */
}
printf (" - %d values\n",nval); /* output no. values in line */
}
(注意:strtol()
比转换失败的sscanf()
提供更好的错误报告)
如果将其与一个示例结合在一起,该示例从提供给程序的第一个参数的文件名中读取(如果没有给出参数,则默认从stdin
读取),您可以这样做:
#include <stdio.h>
#define MAXC 1024 /* if you need a constant,#define one (or more) */
int main (int argc,char **argv) {
char line[MAXC] = ""; /* buffer to hold each line */
/* 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 (line,nval); /* output no. values in line */
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
使用/输出示例
使用文件名dat/nvals.txt
中显示的数据,您将得到:
$ ./bin/fgetsnvals dat/nvals.txt
1 2 3 - 3 values
5 7 10 12 - 4 values
8 9 14 13 15 17 - 6 values
仔细检查一下,如果还有其他问题,请告诉我。