csv 无法使用fscanf读取二进制文件

ff29svar  于 2023-05-11  发布在  其他
关注(0)|答案(2)|浏览(183)

我正在写一个程序,应该是序列化/反序列化CSV到BIN/BIN到CSV在C。目前,我正在第三天与反序列化斗争,因为我尝试了所有我能做的,我决定在这里问。
我做序列化的方式如下:

void serialize(FILE* input_file,FILE* output_file)
{
    char buffer[MAX_LINE_LENGTH];
    struct order
    {
        char* row_id;
        char* order_id;
        char* order_date;
        char* customer_id;
        char* city;
        char* state;
        char* postal_code;
        char* region;
        char* product_id;
        char* category;
        char* sub_category;
        char* price;
    } ;

    struct order order_buffer = {0};

    fgets(buffer,MAX_LINE_LENGTH, input_file);
    
    while(fgets(buffer, MAX_LINE_LENGTH, input_file) != NULL)
    {
        char* token = NULL;

        token = strtok(buffer, ",");
        order_buffer.row_id = token;

        token = strtok(NULL, ",");
        order_buffer.order_id = token;

        token = strtok(NULL, ",");
        order_buffer.order_date = token;

        token = strtok(NULL, ",");
        order_buffer.customer_id = token;

        token = strtok(NULL, ",");
        order_buffer.city = token;

        token = strtok(NULL, ",");
        order_buffer.state = token;

        token = strtok(NULL, ",");
        order_buffer.postal_code = token;

        token = strtok(NULL, ",");
        order_buffer.region = token;

        token = strtok(NULL, ",");
        order_buffer.product_id = token;

        token = strtok(NULL, ",");
        order_buffer.category = token;

        token = strtok(NULL, ",");
        order_buffer.sub_category = token;

        token = strtok(NULL, ",");
        order_buffer.price = token;

        fwrite(&order_buffer, sizeof(order_buffer), 1, output_file);
    }
}

这段代码逐行获取CSV文件并在逗号上标记它。
之后,它将其存储到一个结构体中(本次迭代中的所有char数组,尽管其中一些是int)
到这里为止一切都很好,我能够序列化CSV文件并将其大小从1.2MB减少到大约470 kb。
但是,我不能反序列化它。
我尝试了几种不同的方法,最后我决定fscanf是最好的,下面是代码:

void deserialize(FILE* input_file, FILE* output_file)
{
    char buffer[MAX_LINE_LENGTH];
    struct order
    {
        char* row_id;
        char* order_id;
        char* order_date;
        char* customer_id;
        char* city;
        char* state;
        char* postal_code;
        char* region;
        char* product_id;
        char* category;
        char* sub_category;
        char* price;
    } ;

    struct order order_buffer = {0};
    char* line[MAX_LINE_LENGTH];

    fscanf(input_file, "%s%s%s%s%s%s%s%s%s%s%s%s",
           order_buffer.row_id,
           order_buffer.order_id,
           order_buffer.order_date,
           order_buffer.customer_id,
           order_buffer.city,
           order_buffer.state,
           order_buffer.postal_code,
           order_buffer.region,
           order_buffer.product_id,
           order_buffer.category,
           order_buffer.sub_category,
           order_buffer.price);

    printf("%s", order_buffer.order_id);

    printf("%s", order_buffer.row_id);
}

该函数可以创建一个新的csv文件,但不向其中写入任何内容,也不向结构体分配任何值。
我猜这和char数组未初始化有关,但除此之外,我完全迷路了。
我做错了什么?

bq3bfh9z

bq3bfh9z1#

您正在初始化一个订单结构,其中指针指向一些文本字符串数据....

struct order
    {
        char* row_id;
        char* order_id;
        char* order_date;
        char* customer_id;
        char* city;
        char* state;
        char* postal_code;
        char* region;
        char* product_id;
        char* category;
        char* sub_category;
        char* price;
    };

然后将指针写入磁盘。您没有编写它们指向的文本字符串。

fwrite(&order_buffer, sizeof(order_buffer), 1, output_file);

如果您要将CSV数据解析为有意义的内容,即将state的文本字符串解析为一个枚举:

enum State {
       NSW,
       Victoria, 
       Tasmania, 
       Queensland,
    };

    State text_to_state(const char* text) {
       if(!strcmp(text, "NSW")) return NSW;
       if(!strcmp(text, "Tasmania")) return Tasmania;
       /* snip */

       // bad input
       abort(); 
    }
    
    struct order
    {
        State state;
    };

然后,在fwrite/fread上抛出它应该没问题,不需要额外的工作。
如果像'product_id'这样的东西很好地Map为一个整数,那么在结构中使用整数!

int text_to_integer(const char* text) {
       return atoi(text)
    }
    
    struct order
    {
        State state;
        int product_id;
    };

如果你需要一个实际的文本字符串作为数据结构的一部分,你要么需要使用一个固定大小的char数组,要么更聪明(例如fwrite字符串大小,然后fwrite实际字符串数据)。

struct order
    {
        State state;
        int product_id;
        char customer_name[64];
    };

对于像'price'这样的东西,要么使用十进制类型,要么以便士/美分的倍数存储,并使用整数。

while(fgets(buffer, MAX_LINE_LENGTH, input_file) != NULL)
    {
        char* token = NULL;

        /* snip */

        token = strtok(buffer, ",");
        order_buffer.state = text_to_state(token);

        /* snip */

        token = strtok(NULL, ",");
        order_buffer.product_id = text_to_integer(token);

        /* snip */

        token = strtok(NULL, ",");
        strcpy(order_buffer.customer_name, token);

        /* snip */

        // then, and only then, will a binary file make sense. 
        fwrite(&order_buffer, sizeof(order_buffer), 1, output_file);
    }

然后,当你读回它,所有的数据,你期望将在那里,例如。

// then, and only then, will a binary file make sense. 
        fread(&order_buffer, sizeof(order_buffer), 1, output_file);

但是,如果您想将所有内容保存为文本字符串,只需使用原始CSV文本文件作为存储....

guicsvcw

guicsvcw2#

有两个逻辑错误。

struct order
{
    char* row_id;
    char* order_id;
    char* order_date;
    char* customer_id;
    char* city;
    char* state;
    char* postal_code;
    char* region;
    char* product_id;
    char* category;
    char* sub_category;
    char* price;
};

输出为fwrite(&order_buffer, sizeof(order_buffer), 1, output_file);
它只会将char *指针写入文件,而不是它们指向的内存。不会是你想要的。
另一个错误:

fscanf(input_file, "%s%s%s%s%s%s%s%s%s%s%s%s",...);

%s将停止匹配,直到' ''\t''\n'
您使用fwrite()编写内容,它不会添加空白字符。
然后第一个%s将匹配整个字符串,直到EOF被调用时。
另一个潜在错误是混合fwrite()fscanf()时字节对齐。

struct order {
    char *str_1; // 8 bytes on 64bit os
    int8_t a; // 8 bytes too.
};

fread()通常与fwrite()一起使用。并且fscanf()fprintf()一起使用。
如果使用fscanf(),最好在fprintf()中添加空格或宽度。
例如:

fprintf (outfile, "%s %s", hello, world);
fscanf (infile, "%s %s", &hello, &world);
//OR
fprintf (outfile, "%10s%8s", hello, world);
fscanf (infile, "%10s%8s", &hello, &world);

相关问题