我有一个读取和解析文件的函数,但是valid_lines计数器没有增加,尽管它下面的printf按预期工作。我尝试注解掉函数的不同部分,看起来if (sscanf...
块可能导致了这个问题。
void read_file(char *filename, int *city_num_lines, double *city_min, double *city_max, double *city_avg) {
// files are in data_files/ directory
char path[LINE_MAX_LENGTH] = "data_files/";
strcat(path, filename);
FILE *file = fopen(path, "r");
// check if file exists
if (file == NULL) {
printf("Error: Could not open file %s\n", filename);
return;
}
char buffer[LINE_MAX_LENGTH];
int valid_lines = 0;
double min = BIG_NUM;
double max = -BIG_NUM;
double sum = 0;
double avg = 0;
// read line by line
while (fgets(buffer, LINE_MAX_LENGTH, file) != NULL) {
// skip first line
if (buffer[0] == 'm') {
continue;
}
// separate by tab (\t)
double cur_max, cur_min;
char delim;
if (sscanf(buffer, "%lf%[\t]%lf", &cur_max, &delim, &cur_min) != 3) {
// printf("skipped line %s", buffer);
continue;
}
// increment line count
valid_lines++;
printf("num_lines incremented\n");
// update min and max
if (cur_max > max) {
max = cur_max;
printf("max: %lf\n", max);
}
if (cur_min < min) {
min = cur_min;
printf("min: %lf\n", min);
}
sum += (cur_max + cur_min) / 2;
// printf("%lf - %lf\n", min, max);
}
printf("num_lines: %d\n", valid_lines);
// calculate average
avg = sum / valid_lines;
// update city values
*city_num_lines = valid_lines;
*city_min = min;
*city_max = max;
*city_avg = avg;
fclose(file); // close file
}
这里是控制台输出(最后几行,因为它永远继续下去)
...
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines incremented
num_lines: 1
有人知道为什么会发生这种事吗?
更新:问题的根本原因是sscanf中的"%lf%[\t]%lf"
,因为我只给了它1个空格的字符,但它以NUL字符结束,导致它影响内存的另一部分,在本例中是valid_lines
变量。
3条答案
按热度按时间yb3bgrhw1#
%[\t]
转换将写入TAB字符,外加一个终止NUL字符,但您只给了它一个字符的空间。yxyvkwin2#
OP的代码导致缓冲区溢出,因为
char delim
太小,甚至无法容纳"\t"
的 string。不要使用没有 * 宽度 * 的
"%[...]"
或"%s"
。以上将接受数字文本之间的1个或多个
"\t"
,因为"%1[\t]"
需要1且仅1个'\t'
,并且以下"%lf"
将消耗0个或多个前导空格,包括制表符-然后是数字。如果代码需要使用
scanf()
并读取数字之间的1个且仅1个制表符,请考虑使用"%n"
来记录扫描的偏移量。使用"%n%*1[\t] %n"
,除1个制表符之外的任何内容都将失败n1 + 1 != n2
。或者,使用
strtod()
、strchr()
、strtok()
、strcspn()
等进行解析。sycxhyv73#
1.检查文件名的大小,否则
strcat()
可能导致缓冲区溢出。使用snprintf()
可能更容易,然后检查生成的path
是否太长(定义为LINE_MAX_LENGTH -1或更长)。fgets()
将返回多个对长行的部分读取。严格解析器将捕获此错误。delim
是1个字节,但您至少需要2个字节。此外,阅读串时,请始终使用最大字段宽度。我建议您忽略带有“*”的分隔符,如下所示:scanf()
将忽略前导空格,因此您将永远无法让它严格按照您所说的那样解析您的输入。1.在执行
avg = sum / valid_lines
之前,您可能需要防止valid_line == 0。使用1.txt的示例输入文件,其中只有最后一行有效:
示例会话返回: