简体   繁体   English

从文件中读取 192/200 行文本后,第二个 fgets 返回分段错误

[英]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.我正在尝试从文本文件中读取 200 行文本,并将每行的值存储到一个结构中。 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.它第一次完美运行,但是当用户说是读取不同的文件时,它只读取并存储文本文件的 200 行中的前 192 行,然后返回一个段错误。

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.我尝试在整个代码的各个部分打印出随机字母,以确定哪一行返回了段错误,并且(根据我发现的内容),这是进入内部的 while 条件,我无法弄清楚为什么它会给出段错误。

#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() : 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).它应该读取、存储每行的部分内容并打印出文本文件的所有 200 行,但它仅适用于前 192 行(第二次使用它时,当用户要求更改文本文件时)。 The format of 1 text line is supposed to be: 1个文本行的格式应该是:

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

(ex. 1 John 89,950 Mary 91,668 ) (例如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您应该采取一切预防措施来避免缓冲区溢出:使用snprintf而不是sprintfstrcat
  • popNames.year is uninitialized when you compute the filename计算文件名时popNames.year未初始化
  • pass the correct size to fgets将正确的大小传递给fgets
  • check the return value of sscanf()检查sscanf()的返回值
  • pass maximum character counts for %s fields in sscanf() .传递sscanf()%s字段的最大字符数。
  • 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在将文件内容解析到 arrays 的第i个条目之前测试i的值,如果文件异常大,您将获得未定义的行为

Your removeCommas() has undefined behavior because you call strcpy with overlapping strings.您的removeCommas()具有未定义的行为,因为您使用重叠字符串调用strcpy 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:这是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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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