在C中使用fscanf进行分段错误

时间:2019-06-07 06:26:32

标签: c

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

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

我也在DevC ++中执行了该命令,并且它起作用了,但是当我使用gcc时,它给了我segfault错误。

主要

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
...
...

2 个答案:

答案 0 :(得分:1)

有两点要注意(1)永远不要跳过缓冲区大小20不足以使用大量名称。 64可能是合理的大小,然后您选择该合理的大小并 double

接下来,(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(索引)变量与fgets的返回值一起用于控制读取循环。这样就消除了文件中的行数少于names的问题,但是却盲目地从错误状态的流中推动读取,直到发生name迭代为止。通过使用ndx < NUMNM && fgets (buf, MAXC, fp)作为循环控件,您可以在任何一种情况不再为真时优雅地退出,避免 Undefined Behavior

(3)如果要存储每个buffer中的名称,则需要为每个名称分配存储空间,并将buffer复制到新存储空间中,然后再进行下一次迭代或使用先前的名称重新填充buffer时丢失。

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

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

请参阅strspn(3) - Linux manual page,看看您是否可以确切了解其工作原理。

(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

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

我看到两个可能导致不确定行为的原因。

首先,在NULL中可能有infile;其次,超出文件行之一中的缓冲区大小。为了不改变您的大部分逻辑,快速修复方法可能是:

FILE* infile;
char buffer[20];
infile = fopen("pathway", "r");
if (infile!=NULL) {
  for (i=-1; i < name; i++) {
     fscanf(infile, "19%s", buffer);
  }
}

您可以考虑改用fgets。例如,提供从here复制的以下示例:

#include <stdio.h>

int main () {
   FILE *fp;
   char str[60];

   /* opening file for reading */
   fp = fopen("file.txt" , "r");
   if(fp == NULL) {
      perror("Error opening file");
      return(-1);
   }
   if( fgets (str, 60, fp)!=NULL ) {
      /* writing content to stdout */
      puts(str);
   }
   fclose(fp);

   return(0);
}