创建自定义头(元数据)的文件 [英] Create Customize Header(metadata) for files

查看:106
本文介绍了创建自定义头(元数据)的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里,我想创建头,其中包含其他的文件细节,如其他文件的元数据。

这code是工作正常,如果我使用的是静态值结构file_header

如果我使用的malloc 结构file_header 比我在这个code得到的问题。

获取问题 FREAD 。可能是 FWRITE 完美。
code是在这里:

 的#include< SYS / types.h中>
#包括LT&; SYS / stat.h>
#包括LT&;&fcntl.h GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&dirent.h GT;
#包括LT&;&string.h中GT;焦炭路径[1024] =/家/测试/主/集成/测试/ package_DIR//诠释计数= 5;结构文件{    字符* FILE_NAME;
    INT FILE_SIZE;
};typedef结构file_header {    INT FILE_COUNT;
    结构文件的文件[5];
}元数据;
元数据* create_header();诠释主(){
    FILE *文件=的fopen(/家/测试/主/集成/测试/ file.txt的,W);
    元数据*头;
    标题= create_header();
    如果(头!= NULL)
    {
        的printf(头的大小为%d \\ n,sizeof的(头));
    }    如果(文件!= NULL){        如果(的fwrite(&放大器;头,的sizeof(标题),1,文件)。1){
            看跌期权(关于FWRITE短计数);
        }
        FCLOSE(文件);
    }
    文件= FOPEN(/家/测试/主/集成/测试/ file.txt的,RB);
    如果(文件!= NULL){
        元数据包头= {0};
        如果(的fread(安培;头,的sizeof(标题),1,文件)。1){
            看跌期权(关于FREAD短计数);
        }
        FCLOSE(文件);
        的printf(文件名=%s的\\ n,header.file [0] .file_name);
        的printf(文件数=%d个\\ N,header.file_count);
        的printf(文件大小=%d个\\ N,header.file [0] .file_size);
    }
    返回0;
}元数据* create_header()
{
    INT FILE_COUNT = 0;
    DIR * dirp;
    结构的dirent *进入;
    dirp =执行opendir(路径);
    元数据*头=(元数据*)malloc的(的sizeof(元数据));
    而((进入= READDIR(dirp))!= NULL){
        如果(入门级GT&; d_type == DT_REG){/ *如果该条目是一个普通文件* /            报头 - >文件[FILE_COUNT] .file_name =(字符*)malloc的(的sizeof(char)的*的strlen(入门> d_name));
            的strcpy(报头 - >文件[FILE_COUNT] .file_name,入门> d_name);
            //把静态的,而是我对这个逻辑我将在后面适用。
            报头 - >文件[FILE_COUNT] .file_size = 10;
            FILE_COUNT ++;        }
    }
    报头 - > FILE_COUNT = FILE_COUNT;
    closedir(dirp);
    //输出(文件数:%d个\\ N,FILE_COUNT);
    返回头;
}

输出:

 报头的大小为8
在FREAD短计数
文件名=(空)
文件数= 21918336
文件大小= 0

有人请帮我解决了这个问题。

感谢


解决方案

您是在64位机器上工作,因为你的指针是8个字节长。

您正在尝试写出到一个文件中的数据,然后在读回来。你遇到了问题,因为指针不写的非常好。 (更多precisely:指针可以被写入而没有任何问题,但指针仅在当前正在运行的程序的意思,并且很少适于写入磁盘和甚至更很少适用于从磁盘读回)

您code的这部分说明了这个问题:

 结构文件{
    字符* FILE_NAME;
    INT FILE_SIZE;
};typedef结构file_header {
    INT FILE_COUNT;
    结构文件的文件[5];
}元数据;
元数据* create_header();诠释主(){
    FILE *文件=的fopen(/家/测试/主/集成/测试/ file.txt的,W);
    元数据*头;
    标题= create_header();
    如果(头!= NULL)
    {
        的printf(头的大小为%d \\ n,sizeof的(头));
    }

端注释:


  • 请文件名变为一个参数传递给的main(),或者至少,到一个变量。编写出了名的两倍使得它很难改变。

  • 这是件好事,你正在做一些错误检测。不过,我不会去批判它,但有相当大的在它的改进。

主要意见:


  • 您看到报头的大小为8 的输出,因为是一个指针。在的sizeof(元数据)(即标题指向型)要大得多,可能是48个字节,但是这取决于如何你的编译器排列并包中的结构化数据。

     如果(文件!= NULL){
        如果(的fwrite(&放大器;头,的sizeof(标题),1,文件)。1){
            看跌期权(关于FWRITE短计数);
        }
        FCLOSE(文件);
    }


这code写8个字节的数据文件。什么写入是你的变量存储的地址。它不写它指向数据的任何事情。

你会得到更接近你是什么后(但仍然行不通的)是:

 如果(FWRITE(头,sizeof的(*头),1文件)。1){
            看跌期权(关于FWRITE短计数);
        }

这会写48字节点左右到文件中。但是,您的文件将不包含文件名;它只会包含指针到文件名保存在当写入文件的时间。要非常小心这里。如果你看了这个文件,你甚至可以看到它出现的工作,因为该名称可能还未从记忆中消失呢。

要获取文件名到文件,你将不得不单独处理每一个。你必须在一个会议做出决定。例如,您可能决定该名称将是由2个字节的pfixed $ P $ 无符号短包含文件名,L的长度,其次是L + 1个字节包含文件名及其终端NUL数据'\\ 0'。然后,你会写的每个文件数据的其他(固定大小)的部件。而且你会重复此过程为每个文件。相反的操作,读取文件,要求书面结构的认识。在那里你期望一个文件名的时候,你会读到两字节长度,你可以使用长度分配为文件名的空间。然后,你读了L + 1字节到新分配的文件名。然后你读取其他固定长度的数据文件,然后移动到下一个文件。

如果您希望能够做到这一切在一个单一的的fwrite()然后 FREAD() ,你将不得不修改你的数据结构:

 结构文件{
    烧焦FILE_NAME [MAX_PERMITTED_FILENAME_LENGTH]
    INT FILE_SIZE;
};

您已经决定了最大允许文件名长度是什么,但它是固定的。如果你的名字很短,你不使用所有的空间;如果你的名字很长,他们可能会被截断。你的结构尺寸现在显着增加(至少在 MAX_PERMITTED_FILENAME_LENGTH 是一个合理的规模,32和1024字节之间说的) 。但是你可以阅读以及与此写入整个结构在一个单一的操作。



  

感谢您的答复,但我在C新型所以我怎么能做到这一点的事情?


最后,你就可以code就有点像这样。

 的#include< dirent.h>
#包括LT&;&errno.h中GT;
#包括LT&;&STDARG.H GT;
#包括LT&;&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;枚举{MAX_FILES = 5};文件结构
{
    字符* FILE_NAME;
    INT FILE_SIZE;
};typedef结构file_header
{
    INT FILE_COUNT;
    文件结构文件[MAX_FILES]
}元数据;静态无效err_exit(为const char *格式,...);
静态元数据* create_header(为const char *目录);
静态无效release_header(元*头);
静态无效write_header(FILE * FP,常量元*头);
静态元数据* read_header(FILE * FP);
静态无效dump_header(FILE * FP,为const char *标记,常量元*头);INT主(INT ARGC,字符** argv的)
{
    如果(argc个!= 3)
        err_exit(用法:%s文件目录\\ n,argv的[0]);    为const char *名称=的argv [1];
    为const char *路径=的argv [2];
    FILE * FP = FOPEN(姓名,WB);    如果(FP == 0)
        err_exit(无法​​打开文件%s写入(%D:%S)\\ n,名称,错误号,字符串错误(错误));    元数据*头= create_header(路径);
    dump_header(标准输出,要写入的数据,标题);
    write_header(FP,报头);
    FCLOSE(FP); //忽略在关闭错误
    release_header(头);    如果((FP = FOPEN(名称,RB))== 0)
        err_exit(无法​​打开文件%s读取(%D:%S)\\ n,名称,错误号,字符串错误(错误));    元数据* read_info = read_header(FP);
    dump_header(标准输出,数据为已读,read_info);
    release_header(read_info);    FCLOSE(FP); //忽略在关闭错误
    返回0;
}静态元数据* create_header(为const char *路径)
{
    INT FILE_COUNT = 0;
    DIR * dirp =执行opendir(路径);
    结构的dirent *进入;
    如果(dirp == 0)
        err_exit(无法​​打开目录%s(%D:%S)\\ n,路径,错误号,字符串错误(错误));
    元数据*头=(元数据*)malloc的(的sizeof(元数据));
    如果(头== 0)
        err_exit(无法​​malloc的空间标题(%D:%S)\\ n,错误号,字符串错误(错误));    报头 - > FILE_COUNT = 0;
    而((进入= READDIR(dirp))= NULL和放大器;!&安培; FILE_COUNT< MAX_FILES)
    {
        // d_type不可移植 - POSIX说,你只能依靠d_name和的d_ino
        如果(入门级GT&; d_type == DT_REG)
        {/ *如果该条目是一个普通文件* /
            //避免的off-by-之一的分配使用的strdup()
            报头 - >文件[FILE_COUNT] .file_name =的strdup(入门> d_name);
            如果(报头 - >文件[FILE_COUNT] .file_name == 0)
                err_exit(无法​​的strdup()文件%s(%D:%S)\\ n,入门> d_name,错误号,字符串错误(错误));
            //把静态的,而是我对这个逻辑我将在后面适用。
            报头 - >文件[FILE_COUNT] .file_size = 10;
            FILE_COUNT ++;
        }
    }
    报头 - > FILE_COUNT = FILE_COUNT;
    closedir(dirp);
    //输出(文件数:%d个\\ N,FILE_COUNT);
    返回头;
}静态无效write_header(FILE * FP,常量元*头)
{
    如果(FWRITE(安培;报头 - > FILE_COUNT,sizeof的(报头 - >!FILE_COUNT),1,FP)= 1)
        err_exit(关于文件计数写入错误(%d:%S)\\ n,错误号,字符串错误(错误));
    常量结构文件*文件=报头 - >文件;
    的for(int i = 0; I<报头 - > FILE_COUNT;我++)
    {
        无符号短name_len = strlen的(文件[I] .file_name)+ 1;
        如果(的fwrite(&放大器;!name_len,sizeof的(name_len),1,FP)= 1)
            err_exit(关于文件名长度写入错误(%d:%S)\\ n,错误号,字符串错误(错误));
        如果(FWRITE(文件[I] .file_name,name_len,1,FP)!= 1)
            err_exit(关于文件名称写入错误(%d:%S)\\ n,错误号,字符串错误(错误));
        如果(的fwrite(&放大器;文件[Ⅰ] .file_size,sizeof的(文件[I] .file_size),1,FP)= 1!)
            err_exit(文件大小写入错误(%d:%S)\\ n,错误号,字符串错误(错误));
    }
}静态元数据* read_header(FILE * FP)
{
    元数据*头=的malloc(sizeof的(*头));
    如果(头== 0)
        err_exit(无法​​malloc的空间标题(%D:%S)\\ n,错误号,字符串错误(错误));
    如果(FREAD(安培;报头 - > FILE_COUNT,sizeof的(报头 - >!FILE_COUNT),1,FP)= 1)
        err_exit(阅读文件计数错误(%d:%S)\\ n,错误号,字符串错误(错误));
    结构文件*文件=报头 - >文件;
    的for(int i = 0; I<报头 - > FILE_COUNT;我++)
    {
        无符号短name_len;
        如果(的fread(安培;!name_len,sizeof的(name_len),1,FP)= 1)
            err_exit(阅读文件名长度错误(%d:%S)\\ n,错误号,字符串错误(错误));
        文件[I] .file_name =的malloc(name_len);
        如果(文件[I] .file_name == 0)
            err_exit(无法​​malloc的空间,文件名(%D:%S)\\ n,错误号,字符串错误(错误));
        如果(FREAD(文件[I] .file_name,name_len,1,FP)!= 1)
            err_exit(阅读文件名错误(%d:%S)\\ n,错误号,字符串错误(错误));
        如果(的fread(安培;文件[Ⅰ] .file_size,sizeof的(文件[I] .file_size),1,FP)= 1!)
            err_exit(读文件大小错误(%d:%S)\\ n,错误号,字符串错误(错误));
    }
    回报(头);
}静态无效dump_header(FILE * FP,为const char *标记,常量元*头)
{
    常量结构文件*文件=报头 - >文件;
    fprintf中(FP,元数据:%S \\ N标记);
    fprintf中(FP文件数:%d个\\ N,报头 - > FILE_COUNT);
    的for(int i = 0; I<报头 - > FILE_COUNT;我++)
        fprintf中(FP,文件%D:大小%5D,名称%s \\ n,我的文件[I] .file_size,文件[I] .file_name);
}静态无效release_header(元*头)
{
    的for(int i = 0; I<报头 - > FILE_COUNT;我++)
    {
        / *扎普文件名,指向文件名* /
        memset的(报头 - >文件[I] .file_name,0xDD,strlen的(报头 - >文件[I] .file_name)+1);
        免费(报头 - >文件[I] .file_name);
        memset的(安培;报头 - >文件[I] .file_name,0xEE,sizeof的(报头 - >文件[I] .file_name));
    }
    免费(头);
}静态无效err_exit(为const char *格式,...)
{
    va_list的ARGS;
    的va_start(参数,格式);
    vfprintf(标准错误,格式,参数);
    va_end用来(参数);
    出口(EXIT_FAILURE);
}

我编译为 dump_file ,并运行它,如下所示:

  $ dump_file XYZ。
元数据:要写入的数据
文件数:5
文件0:10号,姓名的.gitignore
文件1:10号,姓名args.c
文件2:10号,姓名atob.c
文件3:10号,姓名bp.pl
文件4:10号,姓名btwoc.c
元数据:数据为已读
文件数:5
文件0:10号,姓名的.gitignore
文件1:10号,姓名args.c
文件2:10号,姓名atob.c
文件3:10号,姓名bp.pl
文件4:10号,姓名btwoc.c
$ ODX XYZ
0×0000:05 00 00 00 00 0B 67 2E 69 74 69 67 6E 6F 72 65 ....... gitignore
0×0010:00 0A 00 00 00 07 00 61 72 67 73 63 2E 00 0A 00 ....... args.c ...
0×0020:00 00 07 00 61 74 62 6F 63 2E 00 0A 00 00 00 06 .... atob.c ......
0x0030:00 62 70 70 2E 00 6C 0A 00 00 00 08 00 62 74 77 .bp.pl顺便说一句.......
即0x0040:6F 63 2E 63 00 0A 00 00 00 oc.c .....
0x0049:
$

我也许应该重新命名 err_exit() err_sysexit()并修改了错误处理,使错误号和相应的字符串是该函数内部处理,而不是反复添加错误号字符串错误(错误)来调用到 err_exit()


从评论信息

传输一些相当广泛评论的进入正题:


  

我尝试了上述code,但此后文件中获取分段错误:4 ,这意味着数据写入工作正常,但我有一些问题,数据读取。 Nimit


  
  

我尝试了上述code和我得到一个分段错误,而我从文件中读取数据。 user1089679


哎呀:的valgrind 是给我说是无效的警告写在 release_header()。这会搞砸。这是不难解决的,虽然与MDASH;这是第二个 memset的() release_header()这是造成伤害;我无意中省略符号:

  memset的(报头 - >文件[I] .file_name,0xEE,sizeof的(报头 - >文件[I] .file_name)); //破碎
memset的(安培;报头 - >文件[I] .file_name,0xEE,sizeof的(报头 - >文件[I] .file_name)); //正确

这是固定在code。注意,这两个 memset的()操作是在code键确保如果存储器被重复使用,它不包含previous有效数据,这是一个危险鉴于code最初写指针到磁盘,然后再阅读他们回来。在 memset的()通话不会在正常生产code present。

注意 ODX 是一个自造的十六进制转储程序(Mac OS X中没有一个高清程序默认)。您的系统可能已经有高清十六进制转储,或者你可以尝试的高清或尝试自己的谷歌赋寻找替代品。


  

只是想问问,我想上运行的跨平台这个节目,所以有​​低一点的机器有问题? Nimit


有与在任大端或小端机器,该code没有问题;有会是问题,如果你需要从一个小端(英特尔)机床数据大端(SPARC,PPC,...)的机器,反之亦然。在code可能是32位和64位的建立也很敏感;我没有定义字段大小为n位,但作为方便类型如int,可以系统之间切换。如果你想便携式数据,决定该领域的大小(1,2,4,8字节居多,至少在非字符串数据),然后以标准的方式写 - 高位在前(大端)或也许是第一次LSB(little-endian的)。

Here i want to create header which contains other file details like metadata of other files.

This code is works fine if i used static values for struct file_header

If i am using malloc for struct file_header than i am getting problem in this code.

Getting problem in fread. May be fwrite worked perfectly. code is here :

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>



char path[1024] = "/home/test/main/Integration/testing/package_DIR";

//int count = 5;

struct files {

    char *file_name;
    int file_size;
};

typedef struct file_header {

    int file_count;
    struct files file[5];
} metadata;


metadata *create_header();

int main() {
    FILE *file = fopen("/home/test/main/Integration/testing/file.txt", "w");
    metadata *header;
    header = create_header();
    if(header != NULL)
    {
        printf("size of Header is %d\n",sizeof(header));
    }

    if (file != NULL) {

        if (fwrite(&header, sizeof(header), 1, file) < 1) {
            puts("short count on fwrite");
        }
        fclose(file);
    }
    file = fopen("/home/test/main/Integration/testing/file.txt", "rb");
    if (file != NULL) {
        metadata header = { 0 };
        if (fread(&header, sizeof(header), 1, file) < 1) {
            puts("short count on fread");
        }
        fclose(file);
        printf("File Name = %s\n", header.file[0].file_name);
        printf("File count = %d\n", header.file_count);
        printf("File Size = %d\n", header.file[0].file_size);
    }
    return 0;
}

metadata *create_header()
{
    int file_count = 0;
    DIR * dirp;
    struct dirent * entry;
    dirp = opendir(path);
    metadata *header = (metadata *)malloc(sizeof(metadata));
    while ((entry = readdir(dirp)) != NULL) {
        if (entry->d_type == DT_REG) { /* If the entry is a regular file */

            header->file[file_count].file_name = (char *)malloc(sizeof(char)*strlen(entry->d_name));
            strcpy(header->file[file_count].file_name,entry->d_name);
            //Put static but i have logic for this i will apply later.
            header->file[file_count].file_size = 10;
            file_count++;

        }
    }
    header->file_count = file_count;
    closedir(dirp);
    //printf("File Count : %d\n", file_count);
    return header;
}

output :

size of Header is 8
short count on fread
File Name = (null)
File count = 21918336
File Size = 0

anybody please help me to solve out this issue.

Thanks

解决方案

You're working on a 64-bit machine because your pointers are 8 bytes long.

You're trying to write data out to a file and then read it back in. You're running into problems because pointers don't write very well. (More precisely: pointers can be written without any problems, but the pointers only have meaning in the current running program, and are seldom suitable for writing to disk and even more seldom suitable for reading back from disk.)

This part of your code illustrates the problem:

struct files {
    char *file_name;
    int file_size;
};

typedef struct file_header {
    int file_count;
    struct files file[5];
} metadata;


metadata *create_header();

int main() {
    FILE *file = fopen("/home/test/main/Integration/testing/file.txt", "w");
    metadata *header;
    header = create_header();
    if(header != NULL)
    {
        printf("size of Header is %d\n",sizeof(header));
    }

Side comments:

  • Make the file name into an argument to main(), or, at least, into a variable. Writing the name out twice makes it hard to change.
  • It is good that you are doing some error detection. However, I'm not going to critique it, though there is considerable room for improvement in it.

Main comments:

  • You see size of Header is 8 in the output because header is a pointer. The sizeof(metadata) (the type that header points to) is much larger, probably 48 bytes, but it depends on how your compiler aligns and packs data in the structure.

    if (file != NULL) {    
        if (fwrite(&header, sizeof(header), 1, file) < 1) {
            puts("short count on fwrite");
        }
        fclose(file);
    }
    

This code writes 8 bytes of data to the file. What it writes is the address where your header variable is stored. It does not write anything of the data that it points at.

What would get closer to what you are after (but is still not going to work) is:

        if (fwrite(header, sizeof(*header), 1, file) < 1) {
            puts("short count on fwrite");
        }

This will write 48 bytes or thereabouts out to file. However, your file won't contain the file names; it will merely contain the pointers to where the file names were stored at the time when the file was written. Be very careful here. If you read this file, you might even see it appearing to work because the names may not have been erased from memory yet.

To get the file names into the file, you will have to handle each one separately. You'll have to decide on a convention. For example, you might decide that the name will be prefixed by a 2-byte unsigned short which contains the length of the file name, L, followed by L+1 bytes of data containing the file name and its terminal NUL '\0'. Then you'd write the other (fixed size) parts of the per-file data. And you'd repeat this process for each of the files. The converse operation, reading the file, requires understanding of the written structure. At the point where you expect a file name, you'll read the two-byte length, and you can use that length to allocate the space for the file name. Then you read the L+1 bytes into the newly allocated file name. Then you read the other fixed-length data for the file, then move onto the next file.

If you want to be able to do it all in a single fwrite() and then fread(), you are going to have to revise your data structure:

struct files {
    char  file_name[MAX_PERMITTED_FILENAME_LENGTH];
    int   file_size;
};

You get to decide what the maximum permitted filename length is, but it is fixed. If your names are short, you don't use all the space; if your names are long, they may be truncated. Your metadata structure size now increases dramatically (at least if MAX_PERMITTED_FILENAME_LENGTH is a reasonable size, say between 32 and 1024 bytes). But you can read and write the entire metadata structure in a single operation with this.


Thanks for your reply but I am new in C so how can I achieve this thing?

Eventually, you'll be able to code it somewhat like this.

#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { MAX_FILES = 5 };

struct files
{
    char *file_name;
    int file_size;
};

typedef struct file_header
{
    int file_count;
    struct files file[MAX_FILES];
} metadata;

static void err_exit(const char *format, ...);
static metadata *create_header(const char *directory);
static void release_header(metadata *header);
static void write_header(FILE *fp, const metadata *header);
static metadata *read_header(FILE *fp);
static void dump_header(FILE *fp, const char *tag, const metadata *header);

int main(int argc, char **argv)
{
    if (argc != 3)
        err_exit("Usage: %s file directory\n", argv[0]);

    const char *name = argv[1];
    const char *path = argv[2];
    FILE *fp = fopen(name, "wb");

    if (fp == 0)
        err_exit("Failed to open file %s for writing (%d: %s)\n", name, errno, strerror(errno));

    metadata *header = create_header(path);
    dump_header(stdout, "Data to be written", header);
    write_header(fp, header);
    fclose(fp);                     // Ignore error on close
    release_header(header);

    if ((fp = fopen(name, "rb")) == 0)
        err_exit("Failed to open file %s for reading (%d: %s)\n", name, errno, strerror(errno));

    metadata *read_info = read_header(fp);
    dump_header(stdout, "Data as read", read_info);
    release_header(read_info);

    fclose(fp);                     // Ignore error on close
    return 0;
}

static metadata *create_header(const char *path)
{
    int file_count = 0;
    DIR * dirp = opendir(path);
    struct dirent * entry;
    if (dirp == 0)
        err_exit("Failed to open directory %s (%d: %s)\n", path, errno, strerror(errno));
    metadata *header = (metadata *)malloc(sizeof(metadata));
    if (header == 0)
        err_exit("Failed to malloc space for header (%d: %s)\n", errno, strerror(errno));

    header->file_count = 0;
    while ((entry = readdir(dirp)) != NULL && file_count < MAX_FILES)
    {
        // d_type is not portable - POSIX says you can only rely on d_name and d_ino
        if (entry->d_type == DT_REG)
        {   /* If the entry is a regular file */
            // Avoid off-by-one under-allocation by using strdup()
            header->file[file_count].file_name = strdup(entry->d_name);
            if (header->file[file_count].file_name == 0)
                err_exit("Failed to strdup() file %s (%d: %s)\n", entry->d_name, errno, strerror(errno));
            //Put static but i have logic for this i will apply later.
            header->file[file_count].file_size = 10;
            file_count++;
        }
    }
    header->file_count = file_count;
    closedir(dirp);
    //printf("File Count : %d\n", file_count);
    return header;
}

static void write_header(FILE *fp, const metadata *header)
{
    if (fwrite(&header->file_count, sizeof(header->file_count), 1, fp) != 1)
        err_exit("Write error on file count (%d: %s)\n", errno, strerror(errno));
    const struct files *files = header->file;
    for (int i = 0; i < header->file_count; i++)
    {
        unsigned short name_len = strlen(files[i].file_name) + 1;
        if (fwrite(&name_len, sizeof(name_len), 1, fp) != 1)
            err_exit("Write error on file name length (%d: %s)\n", errno, strerror(errno));
        if (fwrite(files[i].file_name, name_len, 1, fp) != 1)
            err_exit("Write error on file name (%d: %s)\n", errno, strerror(errno));
        if (fwrite(&files[i].file_size, sizeof(files[i].file_size), 1, fp) != 1)
            err_exit("Write error on file size (%d: %s)\n", errno, strerror(errno));
    }
}

static metadata *read_header(FILE *fp)
{
    metadata *header = malloc(sizeof(*header));
    if (header == 0)
        err_exit("Failed to malloc space for header (%d:%s)\n", errno, strerror(errno));
    if (fread(&header->file_count, sizeof(header->file_count), 1, fp) != 1)
        err_exit("Read error on file count (%d: %s)\n", errno, strerror(errno));
    struct files *files = header->file;
    for (int i = 0; i < header->file_count; i++)
    {
        unsigned short name_len;
        if (fread(&name_len, sizeof(name_len), 1, fp) != 1)
            err_exit("Read error on file name length (%d: %s)\n", errno, strerror(errno));
        files[i].file_name = malloc(name_len);
        if (files[i].file_name == 0)
            err_exit("Failed to malloc space for file name (%d:%s)\n", errno, strerror(errno));
        if (fread(files[i].file_name, name_len, 1, fp) != 1)
            err_exit("Read error on file name (%d: %s)\n", errno, strerror(errno));
        if (fread(&files[i].file_size, sizeof(files[i].file_size), 1, fp) != 1)
            err_exit("Read error on file size (%d: %s)\n", errno, strerror(errno));
    }
    return(header);
}

static void dump_header(FILE *fp, const char *tag, const metadata *header)
{
    const struct files *files = header->file;
    fprintf(fp, "Metadata: %s\n", tag);
    fprintf(fp, "File count: %d\n", header->file_count);
    for (int i = 0; i < header->file_count; i++)
        fprintf(fp, "File %d: size %5d, name %s\n", i, files[i].file_size, files[i].file_name);
}

static void release_header(metadata *header)
{
    for (int i = 0; i < header->file_count; i++)
    {
        /* Zap file name, and pointer to file name */
        memset(header->file[i].file_name, 0xDD, strlen(header->file[i].file_name)+1);
        free(header->file[i].file_name);
        memset(&header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name));
    }
    free(header);
}

static void err_exit(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(EXIT_FAILURE);
}

I compiled it as dump_file, and ran it as shown:

$ dump_file xyz .
Metadata: Data to be written
File count: 5
File 0: size    10, name .gitignore
File 1: size    10, name args.c
File 2: size    10, name atob.c
File 3: size    10, name bp.pl
File 4: size    10, name btwoc.c
Metadata: Data as read
File count: 5
File 0: size    10, name .gitignore
File 1: size    10, name args.c
File 2: size    10, name atob.c
File 3: size    10, name bp.pl
File 4: size    10, name btwoc.c
$ odx xyz
0x0000: 05 00 00 00 0B 00 2E 67 69 74 69 67 6E 6F 72 65   .......gitignore
0x0010: 00 0A 00 00 00 07 00 61 72 67 73 2E 63 00 0A 00   .......args.c...
0x0020: 00 00 07 00 61 74 6F 62 2E 63 00 0A 00 00 00 06   ....atob.c......
0x0030: 00 62 70 2E 70 6C 00 0A 00 00 00 08 00 62 74 77   .bp.pl.......btw
0x0040: 6F 63 2E 63 00 0A 00 00 00                        oc.c.....
0x0049:
$

I should probably have renamed err_exit() as err_sysexit() and revised the error handling so that errno and the corresponding string were handled inside that function, rather than repeatedly adding errno and strerror(errno) to the calls to err_exit().


Information from comments

Transferring some of the rather extensive commentary into the question:

I tried above code, but getting segmentation fault after File : 4, which means that the data write is working properly but I'm having some problem with data read. Nimit

I tried above code and I'm getting a segmentation fault while I am reading data from file. user1089679

Oops: valgrind is giving me warnings about an invalid write in release_header(). That would screw things up. It isn't hard to resolve, though — it is the second memset() in release_header() that's causing the mischief; I accidentally omitted the ampersand:

memset( header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name));  // Broken
memset(&header->file[i].file_name, 0xEE, sizeof(header->file[i].file_name));  // Correct

This is fixed in the code. Note that both memset() operations are in the code to ensure that if memory is reused, it does not contain previous valid data, which was a risk given that the code was originally writing pointers out to disk and then reading them back again. The memset() calls would not be present in normal production code.

Note that odx is a home-brew hex dump program (Mac OS X does not have an hd program by default). Your system may already have hd for hex dump, or you could try hd or try your own Google Fu to find alternatives.

Just want to ask that, I want to run this program on cross-platform, so is there any problem with low bit machines? Nimit

There is no problem with this code on either big-endian or little-endian machines; there'd be problems if you take data from a little-endian (Intel) machine to a big-endian (SPARC, PPC, ...) machine or vice versa. The code is probably also sensitive to 32-bit vs 64-bit builds; I didn't define field sizes as n-bits but as convenient types like int which can change between systems. If you want portable data, decide on the field sizes (1, 2, 4, 8 bytes, mostly, at least for the non-string data), and then write it in a standard way - MSB first (big-endian) or perhaps LSB first (little-endian).

这篇关于创建自定义头(元数据)的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆