【问题标题】:Segmentation fault (core dumped) when using fscanf in C, call-by-reference used在 C 中使用 fscanf 时出现分段错误(核心转储),使用了引用调用
【发布时间】:2020-10-04 03:46:55
【问题描述】:

我正在尝试编写一个简单的程序来读取文件并打印文件的每一行。每行将包含一个姓名和他们的年龄,例如:

Jenny 16
Amy 24

我遇到了分段错误(核心转储),我不确定是什么导致了这个问题,因为我已经对 fscanf 中的整数值使用了引用调用。将不胜感激任何输入 *对不起,我之前应该指定这个,但输入文件名只是 file1,所以它不应该比缓冲区长

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

int main(){
    char filename[10];
    scanf("%s",filename);
    FILE *file;
    char name[20];
    int num;
    file = fopen("filename", "r");
    while
    ((fscanf(file, "%s %d\n", name, &num)) != EOF) {
    printf("%s %d\n", name, num);
    }
    fclose(file);
    return 0;
}

  

【问题讨论】:

  • 查看fopen的返回值。就目前而言,如果无法打开文件,您将出现段错误。

标签: c segmentation-fault


【解决方案1】:

简单错误 - "filename" 是字符串文字,因此 fopen("filename", "r"); 将尝试打开不存在的文件。将其更改为fopen(filename, "r");,它应该可以工作。请注意,从 fscanf 读取是不安全的 - 如果名称超过 19 个字符,程序可能会以不可预知的方式运行; fopen 可以覆盖程序中的其他数据,可以省略空终止字符,或者程序可能在许多示例中执行得很好,并在其他示例中给出奇怪的结果 - 这称为“未定义行为”。然而,对于简单的学习目的,这种方法就足够了 - 请记住,它可能会不时崩溃。

【讨论】:

  • 非常感谢!我快要疯了,想知道为什么 fscanf 不工作......
【解决方案2】:

您需要限制 scanf 以便它不再读取缓冲区中的任何内容。分段错误可能是因为您正在读取的名称或文件名的字符数超过缓冲区的大小。 (可以肯定的是,您应该发布您的输入数据)。

你应该像这样使用 scanf 系列函数:

scanf("%9s",filename);

请注意,我将大小限制为 9(因为您的缓冲区长度为 10 个字符),以便为 scanf 留出空间来放置 \x00 来终止字符串。

【讨论】:

    【解决方案3】:

    在这种情况下,与fscanf() 一起阅读他非常脆弱。如何处理:

    Jenny 16
    Minnie Mouse        92
    

    如果缺少空格怎么办?

    Jenny16
    Minnie Mouse92
    

    fscanf() 还能用吗?

    关键是使用fgets()(或POSIX getline())以面向行的方式处理输入,将整行读入缓冲区(字符数组),然后解析值你需要从缓冲区使用sscanf()(或一对指针)

    解决此问题的一种方法是将直到第一个数字的所有内容分隔为name,然后转换剩余的age。然后,您只需通过用 nul-terminating 字符覆盖 whtiespace 字符来删除 name 中的所有尾随空格,以修剪 name 中的任何尾随空格。您可以使用strlen() 获取name 中所有字符的长度,然后使用指向最后一个字符的指针并从头开始删除空格,直到找到第一个非空格字符。

    例如:

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    
    #define MAXC 1024       /* if you need a constant, #define one (or more) */
    #define MAXNM  64
    
    int main (int argc, char **argv) {
        
        char buf[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 (buf, MAXC, fp)) {             /* read each line of input */
            char name[MAXNM];                       /* name */
            int age;                                /* age */
            /* parse contents of buf into name and age -- VALIDATE */
            if (sscanf (buf, "%63[^0-9]%d", name, &age) == 2) {
                size_t len = strlen(name);          /* get length of name */
                char *p = name + len - 1;           /* pointer to last char in name */
                
                while (p > name && isspace(*p))     /* while char is whitespace */
                    *p-- = 0;                       /* overwrite with nul-terminating char */
                
                printf ("name: '%s'  age: %d\n", name, age);
            }
        }
        if (fp != stdin) fclose (fp);       /* close file if not stdin */
    }
    

    注意:“缓冲区”没有什么特别之处。它只是一个字符数组(或unsigned charuint8_t),足以容纳您的全部身份需要临时存储——不要吝啬缓冲区大小。如果在内存有限的微控制器上,您可以根据需要向下调整)

    也不要硬编码文件名数字(称为幻数),除非别无选择 --- 提供字段时如上所述-width 修饰符到%[^0-9] 转换说明符。否则,如果您需要一个常量#define,或者如果您有多个常量要定义,则使用全局enum

    使用/输出示例

    使用dat/name_age.txt 中的原始示例,您将拥有:

    $ ./bin/name_age dat/name_age.txt
    name: 'Jenny'  age: 16
    name: 'Amy'  age: 24
    

    或者在"Minnie Mouse 92"的例子中:

    $ ./bin/name_age <dat/name_age2.txt
    name: 'Jenny'  age: 16
    name: 'Minnie Mouse'  age: 92
    

    检查一下,如果您有任何问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-24
      • 1970-01-01
      • 2019-12-06
      相关资源
      最近更新 更多