【问题标题】:How to write and read dynamic arrays from binary file如何从二进制文件中写入和读取动态数组
【发布时间】:2018-07-05 16:28:20
【问题描述】:

我有这个结构

    struct _recipe
{
    char name[50];
    char** ingredients;
    char diff[12];
    int time;
    int calories;
    char** procedure;   
} recipe;

我想复制二进制文件中的所有数据。 首先,我已经为成分和程序动态分配了内存,并且我已经编写了我需要的所有内容。但是我需要将所有内容都写在一个二进制文件中。我知道它们都是指针,所以这意味着如果我使用

fwrite(&recipe,sizeof(recipe),1,fbr);

我将在文件中写入地址,而不是我需要的实际值。我已经尝试以这种方式在文件中编写结构的每个字段

    fwrite(recipe.name,sizeof(recipe.name),1,fbr);

    fgets(recipe.ingredients[j],30,stdin);
            strcpy(buff,recipe.ingredients[j]);
            len = strlen(buff);
            fwrite(buff,sizeof(recipe.ingredients[0]),len,fbr);

   fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
   fwrite(&recipe.time,sizeof(recipe.time),1,fbr);
   fwrite(&recipe.calories,sizeof(recipe.calories),1,fbr);

    fgets(recipe.procedure[i],1000,stdin);
            strcpy(buff,recipe.procedure[i]);
            len = strlen(buff);
            fwrite(buff,sizeof(recipe.procedure[0]),len,fbr);

我不确定这是否正确,但我尝试将字符串放入另一个字符串,然后将其复制到文件中。问题是,我不确定它是否有效,因为我不知道应该使用什么样的命令来读取我存储的所有值。当然,它的名称有效,我对此没有任何问题,但是当我要阅读成分时,我阻止了自己,因为我将值写在另一个字符串中并且我不知道我应该输入什么长度阅读。也许我错过了一些东西,也许我一开始就在写东西,但我现在不知道该怎么办。

【问题讨论】:

  • 您可以使用诸如 linux 命令od -c 之类的实用程序查看文件的内容。对于成分,您需要更改 sizeof 运算符,因为它不会告诉您成分字符串有多大。您可能想写出整个字符串加上一个空终止符,以便知道下一个成分何时开始。与procedure 字段相同。您还应该寻找有关编组和解组数据结构的帖子。
  • @bruceg 由于我没有 linux,我想我无法阅读它们,除非我在程序中打开它们。反正写行会变成这样? fwrite(buff,sizeof(buff),1,fbr)?对不起,我对此有点困惑,还有阅读程序
  • 如果你使用的是windows,你可以安装cygwin并获得酷炫的linux工具。考虑您的电话fwrite(buff,sizeof(recipe.ingredients[0]),len,fbr);。您将写出 len 个元素,其中 len 是 strlen 的结果。这意味着,您还没有写出空终止符。您还想写出每种成分,而不仅仅是第一种。你如何在你的结构中说明有多少成分或程序?
  • @bruceg 所以我应该写 fwrite(buff, sizeof(recipe ingredients[i], len,fbr) 其中 len 是 strlen 加上空终止符?
  • 是的。这对我来说似乎是正确的。这样,您将写出空终止符。然后当你读回你的文件时,你可以通过空终止符来告诉每个成分或程序的结束。另外,我有点误读了您的代码。您的变量 buff 是适合循环的成分或程序。

标签: c struct binaryfiles dynamic-arrays


【解决方案1】:

你想做这样的事情来写出你的食谱:

    fwrite(recipe.name,sizeof(recipe.name),1,fbr);
    for (int i = 0; i < num_ingredients; i++) {
            int len = strlen(recipe.ingredients[i]) + 1;
            int num_elements = fwrite(recipe.ingredients[i],sizeof(char),len,fbr);
            printf("wrote %d elements for %s\n", num_elements, recipe.ingredients[i]);
    }
    fwrite(recipe.diff,sizeof(recipe.diff),1,fbr);
    for (int i = 0; i < num_procedures; i++) {
            int len = strlen(recipe.procedure[i]) + 1;
            int num_elements = fwrite(recipe.procedure[i],sizeof(char),len,fbr);
            printf("wrote %d elements for %s\n", num_elements, recipe.procedure[i]);
    }

当然,您还需要写出卡路里和时间。在将任何内容读入结构之前,我也会这样做:

memset(&recipe, 0, sizeof(struct _recipe));

如果您使用 fprintf 以 \n 为分隔符写出卡路里和时间,您的食谱文件将如下所示:

$ od -c recipe.bin
0000000   m   u   f   f   i   n   s  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000060  \0  \0   f   l   o   u   r  \0   s   u   g   a   r  \0   e   g
0000100   g   s  \0   v   a   n   i   l   l   a  \0   n   u   t   s  \0
0000120   n   o       d   i   f   f  \0  \0  \0  \0  \0   m   i   x  \0
0000140   b   a   k   e  \0   r   e   s   t  \0   6   0  \n   2   5   0
0000160  \n

【讨论】:

  • 你能解释一下为什么在从结构中读取任何内容之前我需要 memset 吗? fprintf 也不是只用于文本文件吗?
  • 如果你不做memset,你会在你的recipe名字后面打印出随机的垃圾字符。这样,您将获得所有零字符。 fprintf 应该适用于二进制文件。但是,您可以继续使用 fwrite。我只是使用 fprintf 使文件内容更易于阅读。
  • 我明白了,但是成分的数量和程序的数量呢?写之前不知道
  • 另外,读数只是fread(&amp;recipe, sizeof(recipe), 1,fbr) ,还是我每次都需要读取每个值?
  • 没有。你不能那样读。想象一下将结构覆盖在我上面粘贴的二进制文件之上。配方结构的成分成员是char **。因此,您需要读入所有成分以了解有多少成分,然后分配成分数组。分配成分数组后,您可以将数组中的每个点设置为指向您已读入的字符串。另一种方法是写出文件头,指定文件中有多少成分和程序。
【解决方案2】:

您可以设计一个简单的存储protocol 用于写入/读取。

例如:

| struct recipe | t l v | t l v | ... | t l v | struct recipe | t l v | ... | t l v |

|<-      a complete struct recipe data      ->|


| description | t(ype) | l(ength) | v(alue)|
| ingredient  | i      | strlen() | string |
| procedure   | p      | strlen() | string |
| data end    | e      | 0        | <null> |

读取配方数据:

1. Read a struct recipe.
2. Read a t, t=='i' -- go (3.), t=='p' -- go (4.), t=='e' -- go (5.).
3. Read a l, then read a string whose length is l as an ingredient. Go (2.).
4. Read a l, then read a string whose length is l as a procedure. Go (2.).
5. Finish.

写入配方数据:

1. Write a struct recipe.
2. Write ingredients to `i strlen(ingredient) ingredient` one by one.
3. Write procedures to `p strlen(procedure) procedure` one by one.
4. Write `e 0`.
5. Finish.

伪代码:(只是一个demo,请自行设计protol-

void write_recipe(recipe *r, FILE *fp)
{
    fwrite(r, sizeof(recipe), 1, fp);
    // ...
    for ()
    {
        sprintf(buff, "i %5d %s", strlen(r->ingredients[i]), r->ingredients[i]);
        fwrite(buff, strlen(buff), 1, fp);
    }
    sprintf(buff, "e 0");
    fwrite(buff, strlen(buff), 1, fp);
    //...
    for ()
    {
        sprintf(buff, "p %5d %s", strlen(r->procedure[i]), r->procedure[i]);
        fwrite(buff, strlen(buff), 1, fp);
    }
    sprintf(buff, "e 0");
    fwrite(buff, strlen(buff), 1, fp);
    // ...
}

void read_recipe(FILE *r, FILE *fp)
{
    fread(r, sizeof(recipe), 1, fp);
    // ...
    while (true)
    {
        fread(&t, sizeof(char), 1, fp);
        if (t == 'e')
        {
            // ...
            break;
        }
        fread(buff, sizeof(char), 7, fp);
        sscanf("%d", &len);
        fread(buff, sizeof(char), len, fp);
        buff[len] = 0;

        ingredient = malloc(sizeof(char) * (len + 1));
        strcpy(ingredient, buff);
        // ...
    }
    // ...
    while (true)
    {
        fread(&t, sizeof(char), 1, fp);
        if (t == 'e')
        {
            // ...
            break;
        }
        fread(buff, sizeof(char), 7, fp);
        sscanf("%d", &len);
        fread(buff, sizeof(char), len, fp);
        buff[len] = 0;

        procedure = malloc(sizeof(char) * (len + 1));
        strcpy(procedure, buff);
        // ...
    }
    // ...
}

【讨论】:

  • 对不起,我没有完全理解你的回答
猜你喜欢
  • 2022-01-28
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 2019-10-16
  • 2014-01-07
  • 2015-06-16
  • 1970-01-01
相关资源
最近更新 更多