C 使用fwrite/fread和数据结构的几个问题

C 使用fwrite/fread和数据结构的几个问题,c,data-structures,file-io,fread,fwrite,C,Data Structures,File Io,Fread,Fwrite,我第一次使用fwrite()和fread()将一些数据结构写入磁盘,我对最佳做法和正确的操作方法有几个问题 我在磁盘上写的是插入到图形结构中的所有用户配置文件(以便以后读取)。每个图形顶点的类型如下: typedef struct sUserProfile { char name[NAME_SZ]; char address[ADDRESS_SZ]; int socialNumber; char password[PASSWORD_SZ]; HashTa

我第一次使用
fwrite()
fread()
将一些数据结构写入磁盘,我对最佳做法和正确的操作方法有几个问题

我在磁盘上写的是插入到图形结构中的所有用户配置文件(以便以后读取)。每个图形顶点的类型如下:

typedef struct sUserProfile {
    char name[NAME_SZ];
    char address[ADDRESS_SZ];
    int socialNumber;
    char password[PASSWORD_SZ];

    HashTable *mailbox;
    short msgCount;
} UserProfile;
这就是我目前将所有配置文件写入磁盘的方式:

void ioWriteNetworkState(SocialNetwork *social) {
    Vertex *currPtr = social->usersNetwork->vertices;
    UserProfile *user;

    FILE *fp = fopen("save/profiles.dat", "w");

    if(!fp) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    fwrite(&(social->usersCount), sizeof(int), 1, fp);

    while(currPtr) {
        user = (UserProfile*)currPtr->value;

        fwrite(&(user->socialNumber), sizeof(int), 1, fp);
        fwrite(user->name, sizeof(char)*strlen(user->name), 1, fp);
        fwrite(user->address, sizeof(char)*strlen(user->address), 1, fp);
        fwrite(user->password, sizeof(char)*strlen(user->password), 1, fp);
        fwrite(&(user->msgCount), sizeof(short), 1, fp);

        break;

        currPtr = currPtr->next;
    }

    fclose(fp);
}
注意事项:

  • 您看到的第一个
    fwrite()
    将在图形中写入用户总数,以便我知道需要读取多少数据
  • 中断
    用于测试目的。有成千上万的用户,我仍在试验代码
我的问题:

  • 阅读之后,我决定对每个元素使用
    fwrite()
    ,而不是编写整个结构。我还避免将指针写入邮箱,因为我不需要保存该指针。那么,这就是路吗?多个
    fwrite()
    ,而不是整个结构的全局?不是慢了点吗
  • 如何读回此内容?我知道我必须使用
    fread()
    但是我不知道字符串的大小,因为我使用
    strlen()
    来编写它们。我可以在写入字符串之前写入strlen()的输出,但是没有额外的写入,还有更好的方法吗
      • 它比较慢。调用函数x次比在x>1时调用一次要慢。如果性能是一个问题,您可以使用sizeof(结构)的
        fwrite
        /
        fread
        进行常规使用,并编写可移植的序列化版本进行导入/导出。但首先要检查这是否真的是个问题。大多数格式不再使用二进制数据,因此您可以看出,至少性能不是他们主要关心的问题

      • 不,没有。另一种方法是使用基于
        fgetc(3)
        的strlen

          • 它比较慢。调用函数x次比在x>1时调用一次要慢。如果性能是一个问题,您可以使用sizeof(结构)的
            fwrite
            /
            fread
            进行常规使用,并编写可移植的序列化版本进行导入/导出。但首先要检查这是否真的是个问题。大多数格式不再使用二进制数据,因此您可以看出,至少性能不是他们主要关心的问题

          • 不,没有。另一种方法是使用基于
            fgetc(3)
            的strlen


          你是对的:你现在这样做,没有办法读回内容,因为你不知道一个字符串从哪里结束,下一个字符串从哪里开始

          您为避免对结构化数据使用fwrite()而引用的建议是好的,但将该建议解释为意味着您应该分别对每个元素使用fwrite()可能不是最好的解决方案

          <>我想你应该考虑为你的文件使用不同的格式,而不是用FrEWE()写原始值。(例如,您的文件将无法移植到具有不同字节顺序的计算机。)

          由于您的大多数元素看起来都是字符串和整数,您是否考虑过使用fprintf()进行写入,使用fscanf()进行读取的基于文本的格式?基于文本的格式而不是特定于应用程序的二进制格式的一大优点是,您可以使用标准工具(用于调试等)查看它

          此外,无论您选择何种格式,请确保您考虑将来可能需要添加更多字段的可能性。至少,这意味着您应该在某种类型的头中包含一个版本号,可以是文件本身,也可以是每个条目。更好的方法是标记各个字段(以允许可选属性),例如:

          name: user1
          address: 1600 pennsylvania ave
          favorite color: blue
          
          name: user2
          address: 1 infinite loop
          last login: 12th of never
          

          你是对的:当你现在这样做的时候,没有办法读回内容,因为你不知道一个字符串从哪里结束,下一个字符串从哪里开始

          您为避免对结构化数据使用fwrite()而引用的建议是好的,但将该建议解释为意味着您应该分别对每个元素使用fwrite()可能不是最好的解决方案

          <>我想你应该考虑为你的文件使用不同的格式,而不是用FrEWE()写原始值。(例如,您的文件将无法移植到具有不同字节顺序的计算机。)

          由于您的大多数元素看起来都是字符串和整数,您是否考虑过使用fprintf()进行写入,使用fscanf()进行读取的基于文本的格式?基于文本的格式而不是特定于应用程序的二进制格式的一大优点是,您可以使用标准工具(用于调试等)查看它

          此外,无论您选择何种格式,请确保您考虑将来可能需要添加更多字段的可能性。至少,这意味着您应该在某种类型的头中包含一个版本号,可以是文件本身,也可以是每个条目。更好的方法是标记各个字段(以允许可选属性),例如:

          name: user1
          address: 1600 pennsylvania ave
          favorite color: blue
          
          name: user2
          address: 1 infinite loop
          last login: 12th of never
          

          如果您的程序需要完全可移植,那么您不应该将int和short作为内存块写入磁盘:当您尝试在具有不同字号(例如32位->64位)或不同字节顺序的计算机上读取它们时,数据将被损坏

          对于字符串,可以先写入长度,也可以在末尾包含终止符

          最好的方法通常是使用基于文本的格式。例如,您可以将每条记录作为单独的行写入,字段由制表符或冒号分隔。(作为奖励,您不再需要在文件开头写入记录数——只需在到达文件末尾之前读取记录即可。)

          编辑:但如果这是给你的课堂作业,你可能不需要担心可移植性。将
          '\0'
          终止符从字符串写入磁盘以对其进行分隔。