首页 > 解决方案 > 在 C 中使用 fscanf 的分段错误

问题描述

我正在尝试从 .c 代码中的文本文件中读取名称,但我不断从 fscanf 收到分段错误,并且似乎无法找出问题所在。

我尝试了多种选择,包括增加 char 数组的大小,但我不知道出了什么问题,任何帮助将不胜感激。

我也在 DevC++ 中执行了这个并且它有效,但是当我使用 gcc 时,它给了我这个段错误。

主要是

FILE* infile;
char buffer[20];

在主开关盒中

infile = fopen("pathway", "r");
for (i=-1; i < name; i++)
{
     fscanf(infile, "%s", buffer);
}
fclose(infile);

文件

Zeda'Taxu
Khano'Balme
Goni
...
...

标签: c

解决方案


有几点需要说明 (1)永远不要吝啬缓冲区大小20对于大部分正在使用的名称来说,这是非常不足的。64可能是一个合理的大小,然后你把那个合理的大小加倍

接下来,(2)不清楚name您的代码中做了什么,但由于它用于for循环限制,这似乎是您希望限制读取的名称数量。您必须控制您的读取循环,以便它在 1 时退出 - 您的读取索引不再小于您的name限制&&fgets() 返回一个有效指针,例如

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

#define MAXNM 128   /* if you need a constant, #define one (or more) */
#define NUMNM  10   /*        ( Don't skimp on buffer size! )        */

int main (int argc, char **argv) {

    char buffer[MAXNM];
    size_t ndx = 0, name = NUMNM;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    ...
    while (ndx < name && fgets (buffer, MAXNM, fp)) {
        buffer[strcspn (buffer, "\n")] = 0; /* overwrite \n with nul-char */
        fprintf (stdout, "name[%2zu] : %s\n", ndx++ + 1, buffer);
    }

上面一个简单的ndx(index) 变量与 return fromfgets一起用于控制您的读取循环。这消除了您的行数少于names文件中的行数的问题,然后盲目地从处于错误状态的流中读取,直到name发生迭代。通过ndx < NUMNM && fgets (buf, MAXC, fp)用作循环控件,当任一条件不再为真时,您可以优雅地退出,从而避免Undefined Behavior

(3)如果您打算存储每个名称的名称buffer,则需要为每个名称分配存储空间并buffer在下一次迭代之前复制到新存储空间,否则在重新填充时先前的名称将丢失buffer

(4)fgets读取并包括'\n'填充缓冲区末尾的尾随。你不想存储你的名字,'\n's 悬空结束。strcspn是确定buffer之前的字符数'\n'然后简单地'\n'nul-character覆盖的最简单和最可靠的方法之一。您只需要上面显示的内容,例如

        buffer[strcspn (buffer, "\n")] = 0; /* overwrite \n with nul-char */

请参阅strspn(3) - Linux 手册页,看看您是否能准确了解它是如何工作的。

(5)始终检查每个文件打开(以及写入流后关闭)的返回,并始终检查每个用户输入函数的返回。代码中没有比在尝试处理该数据之前验证从文件或用户接收到的数据更关键的步骤了。否则,您只需轻轻一击键或一个拼写错误,就只是在邀请未定义的行为。

将它完全放在一个程序中,该程序将文件名作为程序的第一个参数打开(或者stdin如果没有提供参数,则默认读取),您可以执行以下操作:

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

#define MAXNM 128   /* if you need a constant, #define one (or more) */
#define NUMNM  10   /*        ( Don't skimp on buffer size! )        */

int main (int argc, char **argv) {

    char buffer[MAXNM];
    size_t ndx = 0, name = NUMNM;
    /* 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 (ndx < name && fgets (buffer, MAXNM, fp)) {
        buffer[strcspn (buffer, "\n")] = 0; /* overwrite \n with nul-char */
        fprintf (stdout, "name[%2zu] : %s\n", ndx++ + 1, buffer);
    }

    if (fp != stdin)    /* close file if not stdin */
        fclose (fp);
}

示例输入文件

现在使用包含 11 个名称的文件练习您的输入例程,比 NUMNM 允许读取的名称多一个,例如

$ cat dat/namesonly.txt
Zeda'Taxu
Khano'Balme
Goni
Ryan,Elizabeth
McIntyre,Osborne
DuMond,Kristin
Larson,Lois
Thorpe,Trinity
Ruiz,Pedro
Ali,Mohammed
Vashti,Indura

示例使用/输出

读取和停止 NUMNM 个名称

$ ./bin/fgetsnames <dat/namesonly.txt
name[ 1] : Zeda'Taxu
name[ 2] : Khano'Balme
name[ 3] : Goni
name[ 4] : Ryan,Elizabeth
name[ 5] : McIntyre,Osborne
name[ 6] : DuMond,Kristin
name[ 7] : Larson,Lois
name[ 8] : Thorpe,Trinity
name[ 9] : Ruiz,Pedro
name[10] : Ali,Mohammed

如果您还有其他问题,请仔细查看并告诉我。


推荐阅读