繁体   English   中英

使用fgets(),strtok(),strcpy()在C中进行分段错误

[英]Segmentation Fault in C using fgets(), strtok(), strcpy()

我从下面显示的代码中遇到段错误。 我正在尝试创建一个简单的数据库,该数据库从bin文件中读取标准输入,并用逗号将其砍掉,然后将每个值放入数组中,然后将其放入结构中。 我想对标准输入中的每一行执行此操作,然后将结构最后写入文件。

我从main调用此loadDatabase函数。

数据看起来像下面一行一样,大约14行:

34156155,MILES,NORMA,TAMMY,n/a,9/16/1964,FEMALE,123-45-6789,LAGUARDIA RD,SHLT,10915,n/a,n/a,CHESTER,NY,848-896-8296,n/a,NMILES@AMGGT.COM,n/a

这是我当前的代码。 如果我的C真的不好,请原谅。 第一次...:

struct _record {
    char ID[25];
    char lname[25]; // last name
    char fname[25]; // first name
    char mname[25]; // middle name
    char suffix[25];    
    char bday[25];  
    char gender[25];    
    char SSN[25];   
    char add1[25]; //address 1
    char add2[25]; //address 2 
    char zip[25];
    char maiden[25];
    char MRN[25];
    char city[25];
    char state[25];
    char phone1[25];
    char phone2[25];
    char email[25];
    char alias[25];
};

bool loadDatabase(char *db_name) {
    printf("Loading Database...");
    char buffer[400];
    FILE *fp;
    int x;
    fp = fopen(db_name, "wb"); //write & binary option      
    if (fp == NULL) {
        puts(" ERROR: FILE CANNOT BE OPENED");
        return false; 
    } else {
        struct _record record; 
        while (fgets(rec, sizeof(rec), stdin) != NULL) {
            value = strtok(NULL, ",");
            flds[0] = strdup(value);

            //load lname
            value = strdup(NULL, ",");
            flds[1] = strdup(value);

            // load fname
            value = strdup(NULL, ",");
            flds[2] = strdup(value);

            // load mname
            value = strtok(NULL, "\n");
            flds[3] = strdup(value);
            // did not write the rest bc of the seg fault

            strcpy(record.ID, flds[0]);
            strcpy(record.lname, flds[1]);
            strcpy(record.fname, flds[2]);
            strcpy(record.mname, flds[3]);
            strcpy(record.suffix, flds[4]);
            strcpy(record.bday, flds[5]);  
            strcpy(record.gender, flds[6]);  
            strcpy(record.SSN, flds[7]);  
            strcpy(record.add1, flds[8]);  
            strcpy(record.add2, flds[9]); 
            strcpy(record.zip, flds[10]);
            strcpy(record.maiden, flds[11]);
            strcpy(record.MRN, flds[12]);
            strcpy(record.city, flds[13]);
            strcpy(record.state,  flds[14]);
            strcpy(record.phone1, flds[15]);
            strcpy(record.phone2, flds[16]);
            strcpy(record.email, flds[17]);
            strcpy(record.alias, flds[18]);
        }
        printf("ID: %s", record.ID);
        fwrite(record, sizeof(struct _record), 1, fp);
        fclose(fp);
    }
    return true;
}

您的代码中存在多个问题:

  • 没有提供fld的定义。 应该将其定义为19个char *的本地数组:

     char *fld[19]; 
  • 您有一些剪切+粘贴错误: value = strdup(NULL, ","); 而不是value = strtok(NULL, ",");

  • 许多行都丢失了。

  • 您永远不会测试strtok()返回NULL 无效的输入将导致未定义的行为

  • 不需要存储字符串:您可以直接将字符串复制到记录字段中。

  • 使用strcpy复制字符串之前,您无需检查字符串的长度。 输入无效可能导致缓冲区溢出。

  • fwrite的参数应该是记录的地址,而不是它的值:

     fwrite(&record, sizeof(struct _record), 1, fp); 
  • 使用strtok() (或带有%[^,]转换说明符的sscanf() )不能正确处理空字段: strtok()将考虑的任何序列,因为单个分隔符( %[^,]将不匹配空字段要么)。 我建议为此使用一个函数。

  • record结构应在每行之前清除,以避免将未初始化的内容存储到数据库文件中。

为了避免这些问题,您应该提高警告级别,并让编译器生成常见编程错误的诊断信息: gcc -Wall -Wextra -Werrorclang -Weverythingcl /W4

这是一个改进的版本:

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

struct _record {
    char ID[25];
    char lname[25]; // last name
    char fname[25]; // first name
    char mname[25]; // middle name
    char suffix[25];
    char bday[25];
    char gender[25];
    char SSN[25];
    char add1[25]; //address 1
    char add2[25]; //address 2
    char zip[25];
    char maiden[25];
    char MRN[25];
    char city[25];
    char state[25];
    char phone1[25];
    char phone2[25];
    char email[25];
    char alias[25];
};

bool loadField(char *dest, int size, char **cursorp) {
    bool truncated = false;
    int i = 0;
    char *p;
    for (p = *cursorp; *p != '\0' && *p != '\n'; p++) {
        if (*p == ',') {
            p++;  // skip the comma separator
            break;
        }
        if (i + 1 < size) {
            dest[i] = *p;
        } else {
            truncated = 1;
        }
        i++;
    }
    // pad the field with null bytes
    while (i < size) {
        dest[i++] = '\0';
    }
    *cursorp = p;
    if (truncated) {
        fprintf(stderr, "field too long: %.*s\n", i, *cursorp);
        return false;
    } else {
        return true;
    }
}

bool loadDatabase(const char *db_name) {
    char buffer[1000];
    FILE *fp;

    printf("Loading Database...");
    fp = fopen(db_name, "wb"); //write & binary option
    if (fp == NULL) {
        fprintf(stderr, "error: cannot open file %s: %s\n", db_name, strerror(errno));
        return false;
    } else {
        struct _record record;    // clear the record
        while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
            char *cursor = buffer;

            memset(&record, 0, sizeof(record));    // clear the record
            loadField(record.ID, sizeof(record.ID), &cursor);
            loadField(record.lname, sizeof(record.lname), &cursor);
            loadField(record.fname, sizeof(record.fname), &cursor);
            loadField(record.mname, sizeof(record.mname), &cursor);
            loadField(record.suffix, sizeof(record.suffix), &cursor);
            loadField(record.bday, sizeof(record.bday), &cursor);
            loadField(record.gender, sizeof(record.gender), &cursor);
            loadField(record.SSN, sizeof(record.SSN), &cursor);
            loadField(record.add1, sizeof(record.add1), &cursor);
            loadField(record.add2, sizeof(record.add2), &cursor);
            loadField(record.zip, sizeof(record.zip), &cursor);
            loadField(record.maiden, sizeof(record.maiden), &cursor);
            loadField(record.MRN, sizeof(record.MRN), &cursor);
            loadField(record.city, sizeof(record.city), &cursor);
            loadField(record.state, sizeof(record.state), &cursor);
            loadField(record.phone1, sizeof(record.phone1), &cursor);
            loadField(record.phone2, sizeof(record.phone2), &cursor);
            loadField(record.email, sizeof(record.email), &cursor);
            loadField(record.alias, sizeof(record.alias), &cursor);
            printf("ID: %s\n", record.ID);
            if (fwrite(&record, sizeof(record), 1, fp) != 1) {
                fprintf(stderr, "error: cannot write record: %s\n", strerror(errno));
                break;
            }
        }
        fclose(fp);
    }
    return true;
}

int main(void) {
    if (loadDatabase("database.bin"))
        return 1;
    return 0;
}

暂无
暂无

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

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