计数器未因sscanf而递增?

owfi6suc  于 2023-03-17  发布在  其他
关注(0)|答案(3)|浏览(196)

我有一个读取和解析文件的函数,但是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变量。

yb3bgrhw

yb3bgrhw1#

%[\t]转换将写入TAB字符,外加一个终止NUL字符,但您只给了它一个字符的空间。

yxyvkwin

yxyvkwin2#

OP的代码导致缓冲区溢出,因为char delim太小,甚至无法容纳"\t"string
不要使用没有 * 宽度 * 的"%[...]""%s"

// separate by tab (\t)
    double cur_max, cur_min;
    // char delim;
    char delim[2];
    //if (sscanf(buffer, "%lf%[\t]%lf", &cur_max, &delim, &cur_min) != 3) {
    if (sscanf(buffer, "%lf%1[\t]%lf", &cur_max, delim, &cur_min) != 3) {
        // printf("skipped line %s", buffer);
        continue;
    }

以上将接受数字文本之间的1个或多个"\t",因为"%1[\t]"需要1且仅1个'\t',并且以下"%lf"将消耗0个或多个前导空格,包括制表符-然后是数字。
如果代码需要使用scanf()并读取数字之间的1个且仅1个制表符,请考虑使用"%n"来记录扫描的偏移量。使用"%n%*1[\t] %n",除1个制表符之外的任何内容都将失败n1 + 1 != n2

int n1, n2;
 if (sscanf(buffer, "%lf%n%*1[\t] %n%lf", &cur_max, &n1, &n2, &cur_min) != 2 || 
     n1 + 1 != n2) {
   printf("skipped line <%s>\n", buffer);
 }

或者,使用strtod()strchr()strtok()strcspn()等进行解析。

sycxhyv7

sycxhyv73#

1.检查文件名的大小,否则strcat()可能导致缓冲区溢出。使用snprintf()可能更容易,然后检查生成的path是否太长(定义为LINE_MAX_LENGTH -1或更长)。

  1. fgets()将返回多个对长行的部分读取。严格解析器将捕获此错误。
  2. delim是1个字节,但您至少需要2个字节。此外,阅读串时,请始终使用最大字段宽度。我建议您忽略带有“*”的分隔符,如下所示:
sscanf(buffer, "%lf%*[\t]%lf", &cur_max, &cur_min) != 2
  1. scanf()将忽略前导空格,因此您将永远无法让它严格按照您所说的那样解析您的输入。
    1.在执行avg = sum / valid_lines之前,您可能需要防止valid_line == 0。
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define BIG_NUM 42
#define LINE_MAX_LENGTH 100

void read_file(char *filename, int *city_num_lines, double *city_min, double *city_max, double *city_avg) {
    char path[LINE_MAX_LENGTH];
    snprintf(path, LINE_MAX_LENGTH, "data_files/%s", filename);
    if(strlen(path) + 1 == LINE_MAX_LENGTH) {
        printf("path name too long\n");
        return;
    }
    FILE *file = fopen(path, "r");
    if (!file) {
        printf("Error: Could not open file %s\n", filename);
        return;
    }

    char buffer[LINE_MAX_LENGTH];
    *city_num_lines = 0;
    *city_min = BIG_NUM;
    *city_max = -BIG_NUM;
    *city_avg = 0;
    while (fgets(buffer, LINE_MAX_LENGTH, file) != NULL) {
        if (buffer[0] == 'm') {
            continue;
        }
        char *p = buffer;
        char *p2;
        // space prefix
        if(isspace(*p)) {
            fprintf(stderr, "skipped line %s", buffer);
            continue;
        }
        double max = strtod(p, &p2);
        // could not parse double, or field is not tab separated
        if(!p2 || (*p2 != '\t' && isspace(*p2))) {
            fprintf(stderr, "skipped line %s", buffer);
            continue;
        }
        p = p2 + 1;
        // 2nd field is space prefixed
        if(isspace(*p)) {
            fprintf(stderr, "skipped line %s", buffer);
            continue;
        }
        double min = strtod(p, &p2); 
        // could not parse double, or we are missing a trailing newline
        if(!p2 || *p2 != '\n') {
            fprintf(stderr, "skipped line %s", buffer);
            continue;
        }
        (*city_num_lines)++;
        if(max > *city_max) *city_max = max;
        if(min < *city_min) *city_min = min;
        *city_avg += (max + min) / 2;
    }
    *city_avg = *city_num_lines ? *city_avg / *city_num_lines : 0;
    fclose(file);
}

int main() {
    int city_num_lines;
    double city_min;
    double city_max;
    double city_avg;
    read_file("1.txt", &city_num_lines, &city_min, &city_max, &city_avg);
    printf("city_num_lines: %d, city_min: %f, city_max: %f, city_avg: %f\n", city_num_lines, city_min, city_max, city_avg);
}

使用1.txt的示例输入文件,其中只有最后一行有效:

m
x
 1  1
2 2
3   3 
4               4
5       5

示例会话返回:

skipped line x
skipped line  1 1
skipped line 2 2
skipped line 3  3 
skipped line 4          4
city_num_lines: 1, city_min: 5.000000, city_max: 5.000000, city_avg: 5.000000

相关问题