简体   繁体   中英

Segmentation Fault using fscanf in C

I am trying to read names from a text file in my code for .c, but I keep getting a segmentation fault from fscanf and can't seem to find out what the problem is.

I tried multiple options, including increasing the char array size, but I have no idea what is wrong, any help would be appreciated.

I also executed this in the DevC++ and it worked, but when I used gcc, it gave me that segfault error.

in main

FILE* infile;
char buffer[20];

in a switch case in main

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

the file

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

There are a couple of points to be made (1) never skimp on buffer size . 20 is horribly insufficient for a large percentage of names in use. 64 may be a reasonable size, and then you take that reasonable size and double it.

Next, (2) it is unclear what name does in your code, but since it is used in a for loop limit, that seems to be the number of names you wish to limit your read to. You must control your read loop so it exits when 1 - your read index is no longer less than your name limit && fgets() returns a valid pointer, eg

#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);
    }

Above a simple ndx (index) variable is used along with the return from fgets to control your read loop. This eliminates your problem of having less lines than names in the file, but then blindly pushing ahead reading from a stream in error-state until name iterations take place. By using ndx < NUMNM && fgets (buf, MAXC, fp) as the loop control, you gracefully exit when either condition in no longer true avoiding Undefined Behavior .

(3) if you intend to store the name from each buffer , you then need to allocate storage for each name and copy buffer to the new storage before your next iteration or the prior name will be lost when buffer is refilled.

(4) fgets reads up to, and includes, the trailing '\\n' at the end of the buffer filled. You do not want to store your names with '\\n' s dangling off the end. strcspn is one of the simplest and most robust ways of determining the number of characters in buffer before the '\\n' and then simply overwriting the '\\n' with the nul-character . You need nothing more than was shown above, eg

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

Refer to strspn(3) - Linux manual page and see if you can figure out exactly how that works.

(5) Always check the return of every file opening (and closing after the stream has been written to), and always check the return of every user-input function. There is no more critical step in your code than validating the data you receive from a file or a user Before you attempt to process that data. Otherwise you are just inviting Undefined Behavior with as little as the slip of a keystroke, or a single misspelling.

Putting it altogether in a program that takes the filename to open as the first argument to the program (or reads from stdin by default if no argument is provided), you could do something like:

#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);
}

Example Input File

Now exercise your input routine with a file containing 11 names, one more than NUMNM allows to be read, eg

$ 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

Example Use/Output

Reading and stopping a NUMNM names

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

Look things over and let me know if you have further questions.

I see two reasons which could lead to undefined behaviour.

First, a possible NULL in infile ; Second, exceeding the buffer size in one of the lines of the file. In order not to change to much of your logic, a quick fix could be:

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

You might consider using fgets instead. Confer, for example, the following example copied from 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); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM