为什么“%79[^\n]\n”会导致分段错误?

ara*_*ebw -1 c gdb

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        char line[80];
        FILE *in = fopen("spooky.csv", "r");
        FILE *file1 = fopen("ufos.csv", "w");
        FILE *file2 = fopen("disappearances.csv", "w");
        FILE *file3 = fopen("others.csv", "w");
        while (fscanf(in, "%79[^\n]\n", line) == 1) {
                if (strstr(line, "UFO"))
                        fprintf(file1, "%s\n", line);
                else if (strstr(line, "Disappearance"))
                        fprintf(file2, "%s\n", line);
                else
                        fprintf(file3, "%s\n", line);
        }
        fclose(file1);
        fclose(file2);
        fclose(file3);
        return 0;
}

Run Code Online (Sandbox Code Playgroud)

此代码返回分段错误的运行时错误。

在此输入图像描述

我在什么硬件上编译这个程序有关系吗?我使用的是带有 Intel Core i7(第七代)的 Fedora 38(工作站)。

chq*_*lie 7

该问题与格式无关fscanf:如错误消息中所示,s内部函数的参数具有指示为该参数传递的空指针__vfscanf_internal的值。0x0FILE *

您应该检查fopen打开文件是否失败,并使用有意义的错误消息报告此情况。

另请注意以下注释:

  • 你应该关闭in
  • 您可以使用fgets()而不是fscanf()格式。
  • 该格式将无法转换spooky.csvfscanf中的初始空行,如果它是实际的 CSV 文件,则不应发生这种情况。
  • 转换规范中的尾随\n将导致换行符与下一行中的任何初始空白一起被消耗,这可能不是有意的。使用%79[^\n]%*[\n]以避免这种情况,但消耗后续的空行。
  • cde 会默默地将长行分成 79 字节的块。修改任意长行的代码并非易事,但更大的缓冲区可能是一个不错的选择。
  • 如果预期第一行是包含字段名称的标题行,您可能需要对第一行进行特殊处理。

这是修改后的版本:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE *fopen_check(const char *filename, const char *mode) {
    FILE *fp = fopen(filename, mode);
    if (fp == NULL) {
        fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
        exit(1);
    }
    return fp;
}

int main(void) {
    char line[80];

    FILE *in = fopen_check("spooky.csv", "r");
    FILE *file1 = fopen_check("ufos.csv", "w");
    FILE *file2 = fopen_check("disappearances.csv", "w");
    FILE *file3 = fopen_check("others.csv", "w");

    fscanf(in, "%*[\n]");  // skip initial empty lines

    while (fscanf(in, "%79[^\n]%*[\n]", line) == 1) {
        if (strstr(line, "UFO"))
            fprintf(file1, "%s\n", line);
        else
        if (strstr(line, "Disappearance"))
            fprintf(file2, "%s\n", line);
        else
            fprintf(file3, "%s\n", line);
    }
    fclose(in);
    fclose(file1);
    fclose(file2);
    fclose(file3);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)