使用fgets()、strtok()和strcpy()在C中出现分段错误

使用fgets()、strtok()和strcpy()在C中出现分段错误,c,database,pointers,strtok,strcpy,C,Database,Pointers,Strtok,Strcpy,我从下面显示的代码中得到一个seg故障。我试图创建一个简单的数据库,从bin文件中读取stdin,用逗号将其切碎,将每个值抛出到数组中,并将其抛出到结构中。我想对标准输入中的每一行执行此操作,然后在最后将结构写入文件 我从main调用这个loadDatabase函数 数据在一行中显示如下,长度约为14行: 34156155,MILES,NORMA,TAMMY,n/a,9/16/1964,FEMALE,123-45-6789,LAGUARDIA RD,SHLT,10915,n/a,n/a,CHES

我从下面显示的代码中得到一个seg故障。我试图创建一个简单的数据库,从bin文件中读取stdin,用逗号将其切碎,将每个值抛出到数组中,并将其抛出到结构中。我想对标准输入中的每一行执行此操作,然后在最后将结构写入文件

我从
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 *fld[19];
    
  • 您有一些剪切+粘贴错误:
    value=strdup(NULL,”,”而不是
    值=strtok(NULL,”,”

  • 许多行丢失了

  • 您从不测试
    strtok()
    是否返回
    NULL
    。无效输入将导致未定义的行为

  • 字符串的内存是不必要的:您可以直接将字符串复制到记录字段中

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

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

    fwrite(&record, sizeof(struct _record), 1, fp);
    
  • <> LI> < > >使用代码> Stotok()/Case>(或<代码> SCANSF)(<代码> > <代码> %[^,] /Case>转换说明符)不正确处理空字段:<代码> Stotok()/Case>将考虑任何代码序列> <代码>作为一个单独的分隔符(<代码> %[^,] />代码>将不匹配空字段)。我建议为此使用函数

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

为了避免这些问题,您应该提高警告级别,让编译器对常见编程错误进行诊断:
gcc-Wall-Wextra-Werror
clang-Weverything
cl/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;
}
#包括
#包括
#包括
#包括
结构记录{
字符ID[25];
char lname[25];//姓
char fname[25];//名字
char mname[25];//中间名
字符后缀[25];
char bday[25];
性别[25];
char-SSN[25];
char add1[25];//地址1
char add2[25];//地址2
char-zip[25];
查尔·梅登[25];
char-MRN[25];
查尔城[25];
半焦态[25];
char-phone1[25];
char-phone2[25];
char电子邮件[25];
字符别名[25];
};
布尔加载字段(字符*dest,整数大小,字符**cursorp){
bool=false;
int i=0;
char*p;
对于(p=*cursorp;*p!='\0'&&&*p!='\n';p++){
如果(*p==','){
p++;//跳过逗号分隔符
打破
}
如果(i+1<尺寸){
dest[i]=*p;
}否则{
截断=1;
}
i++;
}
//用空字节填充字段
而(i
您的代码中存在多个问题:

  • 未提供
    fld
    的定义。应将其定义为19个字符的本地数组*

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

  • 许多行丢失了

  • 你从来都不知道