简体   繁体   中英

2nd fgets returns segmentation fault after reading 192/200 lines of text from file

I'm trying to read in 200 lines of text from a text file and store values on each line into a structure. This is only a snippet of the part that's not working. In the entire code, I ask the user if they wish to read a different text file. It works perfectly the first time but when the user says yes to reading a different file, it only reads in and stores the first 192 lines of the 200 lines of the text file, then returns a segfault.

I tried printing out random letters in various parts throughout the code to determine which line returned the segfault and (from what I've found), it's the while condition with gets inside and I can't figure out why it's giving a segfault.

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

int main(int argc, char *argv[]) {
    FILE *f1;
    int i = 0;
    struct pNames popNames;
    char fileName[20];
    char maleSNumber[20];
    char femaleSNumber[20];
    char string[200];

    fileName[0] = '\0';
    sprintf(fileName, "%d", popNames.year);
    strcat(fileName, "Names.txt");

    if ((f1 = fopen(fileName, "r")) != NULL) {
        while (fgets(string, 200, f1) != NULL) {
            sscanf(string, "%d %s %s %s %s",
                   &popNames.rank[i], 
                   popNames.maleName[i], maleSNumber,
                   popNames.femaleName[i], femaleSNumber);
            printf("%d %s %s %s %s\n", 
                   popNames.rank[i],
                   popNames.maleName[i], maleSNumber,
                   popNames.femaleName[i], femaleSNumber);
            removeCommas(maleSNumber);
            removeCommas(femaleSNumber);
            popNames.maleNumber[i] = atoi(maleSNumber);
            popNames.femaleNumber[i] = atoi(femaleSNumber);
            i++;
        }
    } else {
        printf("Cannot open %s\n", fileName);
        return (-1);
    }
}

Struct Definition:

#define MAXLENGTH 20
#define ROWS 200

/* Struct definitions */
struct pNames {
    int  year;
    int  rank[ROWS];
    char maleName[ROWS][MAXLENGTH];
    int  maleNumber[ROWS];
    char femaleName[ROWS][MAXLENGTH];
    int  femaleNumber[ROWS];
};

The definition for removeCommas() :

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

int removeCommas(char *num) {
    int i;
    int newNum = 0;
    char *pointer;

    pointer = strchr(num, ',');
    while (pointer != NULL) {
        i = (int)(pointer - num);
        strcpy(&num[i], &num[i + 1]);
        newNum += 1;
        pointer = strchr(num, ',');
    }
    return (newNum);
}

It's supposed to read, store parts of each line, and print out all 200 lines of the text file but it's only working for the first 192 lines (the second time you use it, when the user asks to change text files). The format of 1 text line is supposed to be:

[rank][maleName][numMaleNameOccurences][femaleName][numFemaleNameOccurences]

(ex. 1 John 89,950 Mary 91,668 )

I understand you only posted fragments of your code, but here are some visible problems:

  • You should use every precaution to avoid buffer overflows: use snprintf instead of sprintf and strcat
  • popNames.year is uninitialized when you compute the filename
  • pass the correct size to fgets
  • check the return value of sscanf()
  • pass maximum character counts for %s fields in sscanf() .
  • test the value of i before parsing file contents into the i -th entry of the arrays, you will get undefined behavior if the file is unexpectedly large

Your removeCommas() has undefined behavior because you call strcpy with overlapping strings. It is unlikely to be the cause of your segfault, but here is a simplified and corrected version:

int removeCommas(char *num) {
    int newNum = 0;
    char *p = num;

    while ((*num = *p++) != '\0') {
        if (*num != ',')
            num++;
        else
            newNum++;
    }
    return newNum;
}

Here is a modified version of the main() function:

#include <stdio.h>

int main(int argc, char *argv[]) {
    FILE *f1;
    int i;
    struct pNames popNames;
    char fileName[20];
    char maleSNumber[20];
    char femaleSNumber[20];
    char string[200];

    snprintf(fileName, sizeof fileName, "%dNames.txt", popNames.year);

    if ((f1 = fopen(fileName, "r")) == NULL) {
        printf("Cannot open %s\n", fileName);
        return -1;
    }
    i = 0;
    while (i < 200 && fgets(string, sizeof string, f1) != NULL) {
        if (sscanf(string, "%d %19s %19s %19s %19s",
                   &popNames.rank[i], 
                   popNames.maleName[i], maleSNumber,
                   popNames.femaleName[i], femaleSNumber) != 5) {
            printf("invalid input: %s", string);
            continue;
        }
        printf("%d %s %s %s %s\n",
               popNames.rank[i],
               popNames.maleName[i], maleSNumber,
               popNames.femaleName[i], femaleSNumber);
        removeCommas(maleSNumber);
        removeCommas(femaleSNumber);
        popNames.maleNumber[i] = atoi(maleSNumber);
        popNames.femaleNumber[i] = atoi(femaleSNumber);
        i++;
    }
    fclose(f1);
    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