将二进制文件结构化为数组C.

时间:2014-11-23 04:34:06

标签: c arrays struct binaryfiles

我需要将二进制文件中的结构放入一个结构数组中,每个键一个结构。

这是我的结构:

struct candidate{
    char inscr[10];
    char name[44];
    int year;
    int position;
    char curse[30];
};
typedef struct candidate Candidate;

这是我尝试做的事情:

void ordenafile(char fname[13]){
FILE *f = fopen (fname, "rb");
if(f==NULL){
    printf("Error.");
return;
}
fseek(f, 0, SEEK_END);
int  sz = ftell(f);
Candidate *p, *aux,*arr[sz/sizeof(Candidate)];
p=(Candidate*)malloc(sizeof(Candidate));
int i = 0;
while(fread(p,sizeof(candidate),1,f)>0){
    arr[i]=p;
    i++;
}

1 个答案:

答案 0 :(得分:2)

找到fseek()文件的末尾后,您需要在阅读之前快退。

这部分代码没有做你想做的事情:

Candidate *p, *aux,*arr[sz/sizeof(Candidate)];
p=(Candidate*)malloc(sizeof(Candidate));
int i = 0;
while(fread(p,sizeof(candidate),1,f)>0){
    arr[i]=p;
    i++;
}

你有一组结构指针,你分配了一个由p指向的结构。然后,您的循环将值读入一个结构,并将指针复制到指针数组的元素中。因此,数组的所有元素都指向相同的结构。变量aux也未使用。

您需要两种解决方案之一:

Candidate arr[sz/sizeof(Candidate)];
int i;
for (i = 0; fread(&arr[i], sizeof(Candidate), 1, f) == 1; i++)
    ;

这假设您将能够在当前函数中使用该数组并调用它的函数,并且不需要将其返回到调用代码。

可替换地:

Candidate *arr = malloc(sz);
if (arr == 0)
    return 0;
int i;
for (i = 0; fread(&arr[i], sizeof(Candidate), 1, f) == 1; i++)
    ;

return arr;

您可以从函数返回此数组。


工作代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct candidate
{
    char inscr[10];
    char name[44];
    int year;
    int position;
    char curse[30];
};
typedef struct candidate Candidate;

int write_sample_data(const char *filename);
Candidate *read_sample_data(const char *filename, int *number);
void print_sample_data(int num, Candidate *candidates);

int main(void)
{
    const char filename[] = "practice.data";

    if (write_sample_data(filename) != 0)
        fprintf(stderr, "Failed to write sample data\n");
    else
    {
        int number = 0;
        Candidate *clist = read_sample_data(filename, &number);
        if (number == 0)
            fprintf(stderr, "Failed to read sample data\n");
        else
        {
            print_sample_data(number, clist);
            free(clist);
        }
    }
    return 0;
}

int write_sample_data(const char *filename)
{
    FILE *fp = fopen(filename, "wb");
    if (fp == NULL)
    {
        fprintf(stderr, "Failed to open file %s for writing\n", filename);
        return -1;
    }
    Candidate c;
    for (int i = 0; i < 5; i++)
    {
        char inscr[10];
        char name[40];
        char curse[30];
        sprintf(inscr, "I-%.4d", i);
        sprintf(name, "Name %.4d Surname %.4d", rand() % 1000, rand() % 1000);
        sprintf(curse, "Curse %.2d", rand() % 100);
        // Using null-filling property of strncpy()
        strncpy(c.inscr, inscr, sizeof(c.inscr));
        strncpy(c.name, name, sizeof(c.name));
        strncpy(c.curse, curse, sizeof(c.curse));
        c.year = 2010 + i;
        c.position = 1000 * i + (99 - i);
        if (fwrite(&c, sizeof(Candidate), 1, fp) != 1)
        {
            fclose(fp);
            fprintf(stderr, "Failed to write candidate %d\n", i);
            return -1;
        }
    }
    fclose(fp);
    return 0;
}

Candidate *read_sample_data(const char *filename, int *number)
{
    FILE *fp = fopen(filename, "rb");

    if (fp == NULL)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", filename);
        return 0;
    }
    fseek(fp, 0, SEEK_END);
    size_t sz = ftell(fp);
    rewind(fp);

    Candidate *arr = malloc(sz);
    if (arr == 0)
    {
        fprintf(stderr, "Failed to allocate %zu bytes memory\n", sz);
        return 0;
    }

    int i;
    for (i = 0; fread(&arr[i], sizeof(Candidate), 1, fp) == 1; i++)
        ;

    *number = i;
    return arr;
}

void print_sample_data(int number, Candidate *clist)
{
    for (int i = 0; i < number; i++)
    {
        printf("%-10s %-30s %.4d %.4d %s\n",
               clist[i].inscr, clist[i].name, clist[i].year,
               clist[i].position, clist[i].curse);
    }
}

请注意,尽管使用了来自rand()的伪随机数,但每次运行时都会产生相同的数据。另请注意,代码故意利用strncpy()的行为,将具有空字节的字段填充到全长,以便文件中的数据不会产生任何无关紧要的事情。

示例输出:

I-0000     Name 0249 Surname 0807         2010 0099 Curse 73
I-0001     Name 0930 Surname 0658         2011 1098 Curse 72
I-0002     Name 0878 Surname 0544         2012 2097 Curse 23
I-0003     Name 0440 Surname 0709         2013 3096 Curse 65
I-0004     Name 0042 Surname 0492         2014 4095 Curse 87

数据文件的十六进制转储:

0x0000: 49 2D 30 30 30 30 00 00 00 00 4E 61 6D 65 20 30   I-0000....Name 0
0x0010: 32 34 39 20 53 75 72 6E 61 6D 65 20 30 38 30 37   249 Surname 0807
0x0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0030: 00 00 00 00 00 00 00 00 DA 07 00 00 63 00 00 00   ............c...
0x0040: 43 75 72 73 65 20 37 33 00 00 00 00 00 00 00 00   Curse 73........
0x0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0060: 49 2D 30 30 30 31 00 00 00 00 4E 61 6D 65 20 30   I-0001....Name 0
0x0070: 39 33 30 20 53 75 72 6E 61 6D 65 20 30 36 35 38   930 Surname 0658
0x0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0090: 00 00 00 00 00 00 00 00 DB 07 00 00 4A 04 00 00   ............J...
0x00A0: 43 75 72 73 65 20 37 32 00 00 00 00 00 00 00 00   Curse 72........
0x00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x00C0: 49 2D 30 30 30 32 00 00 00 00 4E 61 6D 65 20 30   I-0002....Name 0
0x00D0: 38 37 38 20 53 75 72 6E 61 6D 65 20 30 35 34 34   878 Surname 0544
0x00E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x00F0: 00 00 00 00 00 00 00 00 DC 07 00 00 31 08 00 00   ............1...
0x0100: 43 75 72 73 65 20 32 33 00 00 00 00 00 00 00 00   Curse 23........
0x0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0120: 49 2D 30 30 30 33 00 00 00 00 4E 61 6D 65 20 30   I-0003....Name 0
0x0130: 34 34 30 20 53 75 72 6E 61 6D 65 20 30 37 30 39   440 Surname 0709
0x0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0150: 00 00 00 00 00 00 00 00 DD 07 00 00 18 0C 00 00   ................
0x0160: 43 75 72 73 65 20 36 35 00 00 00 00 00 00 00 00   Curse 65........
0x0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x0180: 49 2D 30 30 30 34 00 00 00 00 4E 61 6D 65 20 30   I-0004....Name 0
0x0190: 30 34 32 20 53 75 72 6E 61 6D 65 20 30 34 39 32   042 Surname 0492
0x01A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x01B0: 00 00 00 00 00 00 00 00 DE 07 00 00 FF 0F 00 00   ................
0x01C0: 43 75 72 73 65 20 38 37 00 00 00 00 00 00 00 00   Curse 87........
0x01D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0x01E0:

结构中有4个字节的填充;在name之后的2和最后的2,所以它的长度是一个方便的96字节,恰好是16的倍数,因此十六进制转储很好地对齐。