我需要从一个我们不知道矩阵尺寸的文件中读取矩阵

时间:2012-11-30 22:58:45

标签: c matrix malloc fwrite fread

我有一个这样的结构

struct Data {
    int ID;
double test_sample[2065][1];
int XX_row;
int XX_col
double **XX;                        //size=[2065][changing]
double **alpha_new;                 //size=[changing][1]
int alpha_new row;
int alpha_new_col;
double t3;
double kernel_par;

}person[20];

我使用fwrite为每个人(20人)编写了这个结构到20个文件中:

fwrite(&person, sizeof( struct Data ), 1,Ptr );

现在我有20个二进制文件。每个文件包含一个人的这些变量。一切都好了。

问题:我无法读取文件并将其分配给某个文件,因为在每个文件中,维度XX和alpha_new矩阵不同。 (在文件[2065] [8]中,其中一些[2065] [12])

我需要使用fread(或不同的)读取这些变量并输入到人脸识别程序...... 有没有办法在文件中单独读取变量,还是应该更改写入方法?

我不知道如何在不使用struct的情况下将所有变量矩阵写入一个文件中!

我希望我能在这里解释我的问题,抱歉我的英语不好,我等着你的帮助 在c完成我的最终项目; 我正在使用visual studio 2012

1 个答案:

答案 0 :(得分:1)

对于如此复杂的结构,它是一项适度的重大任务。这是一个不那么短的SSCCE(Short, Self-Contained, Complete Example)。真的有3个文件撞到了一个:

  • stderr.h - 错误报告功能的声明(前10行)
  • serialize.c - 序列化代码(中间不到300行)
  • stderr.c - 错误报告功能(底部40行)

我不打算解释错误报告功能。就格式化参数而言,它们或多或少地像printf()一样工作,但是它们写入标准错误,而不是标准输出,它们包括程序名称作为前缀,错误来自errnoemalloc()函数检查内存分配,报告错误并在分配失败时退出。这种错误处理适用于简单的程序;对于需要恢复的复杂程序来说,如果出现内存问题,保存工作等等,这是不够的。

在真正的序列化代码中,有4组函数,加上main()来协调。

  1. 用于创建和初始化结构的分配和初始化函数。
  2. 打印转储结构的功能。
  3. 导出功能以序列化要导出的数据。
  4. 导入函数以反序列化要导入的数据。
  5. 打印功能允许人们查看数据,您可以将输出保存到文件,并将导出数据与导入数据进行比较,以确保它们相同。

    如果使用结构来描述所有2D数组,代码会更简单,例如:

    typedef struct Array_2D
    {
        double **data;
        size_t   nrows;
        size_t   ncols;
    } Array_2D;
    

    然后,您只需将其中的3个嵌入struct Data

    struct Data
    {
        int       ID;
        double    t3;
        double    kernel_par;
        Array_2D  test_sample;
        Array_2D  XX;
        Array_2D  alpha_new;
    };
    

    我真的不清楚double test_sample[2065][1];double test_sample[2065];相比的好处。我会观察它会使代码比其他情况更复杂。我最终以double为起点,将其视为&data->test_sample[0][0]的普通一维数组。

    进行序列化的方法不止一种。我已经选择了由N个1D数组表示的二维2D数组,每个1D数组都以size_t为前缀,描述了1D数组的大小。这在文件中提供了一些冗余,这意味着错误检测稍微好一些。简单地输出2D数组的两个维度,然后输出 rows x cols 值是可行的。实际上,有一点,我有导入代码假设虽然导出代码使用了另一种技术 - 当数字被误解并且我得到调试输出和错误时,这并没有令人满意的运行时:

    test_sample: 2.470328e-323, 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00
    2D array size 4617315517961601024 x 5 = 4639833516098453504
    serialize(46983) malloc: *** mmap(size=45035996273704960) failed (error code=12)
    *** error: can't allocate region
    *** set a breakpoint in malloc_error_break to debug
    ./serialize: Out of memory (12: Cannot allocate memory)
    

    那是很多记忆...... 2.470328e-323也是一个麻烦的症状。 (所以不,我第一次运行代码时没有把它弄好。)

    我做了大部分测试,SAMPLE_SIZE为5,NUM_PERSON为3。

    serialize.c

    /* stderr.h */
    #ifndef STDERR_H_INCLUDED
    #define STDERR_H_INCLUDED
    
    static void err_setarg0(char const *argv0);
    static void err_sysexit(char const *fmt, ...);
    static void err_syswarn(char const *fmt, ...);
    
    #endif /* STDERR_H_INCLUDED */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    enum { SAMPLE_SIZE = 20 }; /* 2065 in original */
    enum { NUM_PERSON  = 10 }; /*   20 in original */
    
    struct Data
    {
        int ID;
        double test_sample[SAMPLE_SIZE][1]; //Why?
        size_t XX_row;
        size_t XX_col;
        double **XX;                        //size=[SAMPLE_SIZE][changing]
        double **alpha_new;                 //size=[changing][1]
        size_t alpha_new_row;
        size_t alpha_new_col;
        double t3;
        double kernel_par;
    } person[NUM_PERSON];
    
    typedef struct Data Data;
    
    static void *emalloc(size_t nbytes)
    {
        void *space = malloc(nbytes);
        if (space == 0)
            err_sysexit("Out of memory");
        return space;
    }
    
    static void free_data(Data *data)
    {
        for (size_t i = 0; i < data->XX_row; i++)
            free(data->XX[i]);
        free(data->XX);
    
        for (size_t i = 0; i < data->alpha_new_row; i++)
            free(data->alpha_new[i]);
        free(data->alpha_new);
    
        data->ID = 0;
        data->t3 = 0.0;
        data->kernel_par = 0.0;
        data->XX = 0;
        data->XX_row = 0;
        data->XX_col = 0;
        data->alpha_new = 0;
        data->alpha_new_row = 0;
        data->alpha_new_col = 0;
    }
    
    static void free_array(Data *data, size_t nentries)
    {
        for (size_t i = 0; i < nentries; i++)
            free_data(&data[i]);
    }
    
    static double **alloc_2D_double(size_t rows, size_t cols)
    {
        double **data = emalloc(rows * sizeof(*data));
        for (size_t i = 0; i < rows; i++)
        {
            data[i] = emalloc(cols * sizeof(*data[i]));
        }
        return data;
    }
    
    static void populate_data(Data *data, size_t entry_num)
    {
        /* entry_num serves as 'changing' size */
        data->ID = entry_num;
        data->t3 = entry_num * SAMPLE_SIZE;
        data->kernel_par = (1.0 * SAMPLE_SIZE) / entry_num;
    
        for (size_t i = 0; i < SAMPLE_SIZE; i++)
            data->test_sample[i][0] = i + entry_num;
    
        data->XX_row = SAMPLE_SIZE;
        data->XX_col = entry_num;
        data->XX = alloc_2D_double(data->XX_row, data->XX_col);
    
        for (size_t i = 0; i < data->XX_row; i++)
        {
            for (size_t j = 0; j < data->XX_col; j++)
                data->XX[i][j] = i * data->XX_col + j;
        }
    
        data->alpha_new_row = entry_num;
        data->alpha_new_col = 1;
        data->alpha_new = alloc_2D_double(data->alpha_new_row, data->alpha_new_col);
    
        for (size_t i = 0; i < data->alpha_new_row; i++)
        {
            for (size_t j = 0; j < data->alpha_new_col; j++)
                data->alpha_new[i][j] = i * data->alpha_new_col + j;
        }
    }
    
    static void populate_array(Data *data, size_t nentries)
    {
        for (size_t i = 0; i < nentries; i++)
            populate_data(&data[i], i+1);
    }
    
    static void print_1D_double(FILE *fp, char const *tag, double const *values, size_t nvalues)
    {
        char const *pad = "";
        fprintf(fp, "%s: ", tag);
        for (size_t i = 0; i < nvalues; i++)
        {
            fprintf(fp, "%s%e", pad, values[i]);
            pad = ", ";
        }
        putc('\n', fp);
    }
    
    static void print_2D_double(FILE *fp, char const *tag, double **values, size_t nrows, size_t ncols)
    {
        fprintf(fp, "2D array %s[%zd][%zd]\n", tag, nrows, ncols);
        for (size_t i = 0; i < nrows; i++)
        {
            char buffer[32];
            snprintf(buffer, sizeof(buffer), "%s[%zd]", tag, i);
            print_1D_double(fp, buffer, values[i], ncols);
        }
    }
    
    static void print_data(FILE *fp, char const *tag, const Data *data)
    {
        fprintf(fp, "Data: %s\n", tag);
        fprintf(fp, "ID = %d; t3 = %e; kernel_par = %e\n", data->ID, data->t3, data->kernel_par);
        print_1D_double(fp, "test_sample", &data->test_sample[0][0], sizeof(data->test_sample)/sizeof(data->test_sample[0][0]));
        print_2D_double(fp, "XX", data->XX, data->XX_row, data->XX_col);
        print_2D_double(fp, "Alpha New", data->alpha_new, data->alpha_new_row, data->alpha_new_col);
    }
    
    static void print_array(FILE *fp, char const *tag, const Data *data, size_t nentries)
    {
        fprintf(fp, "Array: %s\n", tag);
        fprintf(fp, "Size: %zd\n", nentries);
        for (size_t i = 0; i < nentries; i++)
        {
            char buffer[32];
            snprintf(buffer, sizeof(buffer), "Row %zd", i);
            print_data(fp, buffer, &data[i]);
        }
        fprintf(fp, "End Array: %s\n\n", tag);
    }
    
    static void set_file_name(char *buffer, size_t buflen, size_t i)
    {
        snprintf(buffer, buflen, "exp_data.%.3zd.exp", i);
    }
    
    static void export_1D_double(FILE *fp, double *data, size_t ncols)
    {
        if (fwrite(&ncols, sizeof(ncols), 1, fp) != 1)
            err_sysexit("Failed to write number of columns");
        if (fwrite(data, sizeof(double), ncols, fp) != ncols)
            err_sysexit("Failed to write array of %zd doubles", ncols);
    }
    
    static void export_2D_double(FILE *fp, double **data, size_t nrows, size_t ncols)
    {
        if (fwrite(&nrows, sizeof(nrows), 1, fp) != 1)
            err_sysexit("Failed to write number of rows");
        if (fwrite(&ncols, sizeof(ncols), 1, fp) != 1)
            err_sysexit("Failed to write number of columns");
        for (size_t i = 0; i < nrows; i++)
            export_1D_double(fp, data[i], ncols);
    }
    
    static void export_int(FILE *fp, int value)
    {
        if (fwrite(&value, sizeof(value), 1, fp) != 1)
            err_sysexit("Failed to write int to file");
    }
    
    static void export_double(FILE *fp, double value)
    {
        if (fwrite(&value, sizeof(value), 1, fp) != 1)
            err_sysexit("Failed to write double to file");
    }
    
    static void export_data(FILE *fp, Data *data)
    {
        export_int(fp, data->ID);
        export_double(fp, data->t3);
        export_double(fp, data->kernel_par);
        export_1D_double(fp, &data->test_sample[0][0], sizeof(data->test_sample)/sizeof(data->test_sample[0]));
        export_2D_double(fp, data->XX, data->XX_row, data->XX_col);
        export_2D_double(fp, data->alpha_new, data->alpha_new_row, data->alpha_new_col);
    }
    
    static void export_array(Data *data, size_t nentries)
    {
        for (size_t i = 0; i < nentries; i++)
        {
            char filename[30];
            set_file_name(filename, sizeof(filename), i);
            FILE *fp = fopen(filename, "w");
            if (fp == 0)
                err_sysexit("Failed to open file %s for writing", filename);
            printf("Export %zd to %s\n", i, filename);
            export_data(fp, &data[i]);
            fclose(fp);
        }
    }
    
    static int import_int(FILE *fp)
    {
        int value;
        if (fread(&value, sizeof(value), 1, fp) != 1)
            err_sysexit("Failed to read int");
        return value;
    }
    
    static double import_double(FILE *fp)
    {
        double value;
        if (fread(&value, sizeof(value), 1, fp) != 1)
            err_sysexit("Failed to read int");
        return value;
    }
    
    static size_t import_size_t(FILE *fp)
    {
        size_t value;
        if (fread(&value, sizeof(value), 1, fp) != 1)
            err_sysexit("Failed to read size_t");
        return value;
    }
    
    static void import_1D_double(FILE *fp, double *data, size_t nvalues)
    {
        size_t size = import_size_t(fp);
        if (size != nvalues)
            err_sysexit("Size mismatch (wanted %zd, actual %zd)\n", nvalues, size);
        if (fread(data, sizeof(data[0]), nvalues, fp) != nvalues)
            err_sysexit("Failed to read %zd doubles");
    }
    
    static void import_2D_double(FILE *fp, double ***data, size_t *nrows, size_t *ncols)
    {
        *nrows = import_size_t(fp);
        *ncols = import_size_t(fp);
        *data  = alloc_2D_double(*nrows, *ncols);
        for (size_t i = 0; i < *nrows; i++)
            import_1D_double(fp, (*data)[i], *ncols);
    }
    
    static void import_data(FILE *fp, Data *data)
    {
        data->ID = import_int(fp);
        data->t3 = import_double(fp);
        data->kernel_par = import_double(fp);
    
        import_1D_double(fp, &data->test_sample[0][0], sizeof(data->test_sample)/sizeof(data->test_sample[0][0]));
        import_2D_double(fp, &data->XX, &data->XX_row, &data->XX_col);
        import_2D_double(fp, &data->alpha_new, &data->alpha_new_row, &data->alpha_new_col);
    }
    
    static void import_array(Data *data, size_t nentries)
    {
        for (size_t i = 0; i < nentries; i++)
        {
            char filename[30];
            set_file_name(filename, sizeof(filename), i);
            FILE *fp = fopen(filename, "r");
            if (fp == 0)
                err_sysexit("Failed to open file %s for reading", filename);
            printf("Import %zd from %s\n", i, filename);
            import_data(fp, &data[i]);
            fclose(fp);
        }
    }
    
    int main(int argc, char **argv)
    {
        err_setarg0(argv[0]);
        if (argc != 1)
            err_syswarn("Ignoring %d irrelevant arguments", argc-1);
        populate_array(person, NUM_PERSON);
        print_array(stdout, "Freshly populated", person, NUM_PERSON);
        export_array(person, NUM_PERSON);
        printf("\n\nEXPORT COMPLETE\n\n");
        free_array(person, NUM_PERSON);
        import_array(person, NUM_PERSON);
        printf("\n\nIMPORT COMPLETE\n\n");
        print_array(stdout, "Freshly imported", person, NUM_PERSON);
        free_array(person, NUM_PERSON);
        return(0);
    }
    
    /* stderr.c */
    /*#include "stderr.h"*/
    #include <stdio.h>
    #include <stdarg.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    
    static char const *arg0 = "<undefined>";
    
    static void err_setarg0(char const *argv0)
    {
        arg0 = argv0;
    }
    
    static void err_vsyswarn(char const *fmt, va_list args)
    {
        int errnum = errno;
        fprintf(stderr, "%s: ", arg0);
        vfprintf(stderr, fmt, args);
        if (errnum != 0)
            fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
        putc('\n', stderr);
    }
    
    static void err_syswarn(char const *fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        err_vsyswarn(fmt, args);
        va_end(args);
    }
    
    static void err_sysexit(char const *fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
        err_vsyswarn(fmt, args);
        va_end(args);
        exit(1);
    }
    

    valgrind下运行时,它被给予一个干净的健康状况,没有内存泄露。在我能够安全地说出来之前,它花了不止一次通过(valgrind显示出一个没有发现结果的错误,虽然一旦检测到它就很明显了。)


    评论中的问题解答

      

    无论如何,执行代码时出现了一些问题。

         

    第一个是'snprintf': identifier not found

         

    第二个是"double **data = emalloc(rows * sizeof(*data));"行,它表示无法从'void *'转换为'double **',这是有道理的,因为data是双倍的emalloc是返回void *;在开始将这些问题嵌入原始程序之前,如何解决这些问题呢?

    1. 不要使用C ++编译器来编译C代码
    2. 使用C99编译器更新到系统。
    3. 或者,因为您可能在Windows上并使用MSVC:

      1. 使用演员double **data = (double **)emalloc(rows * sizeof(*data));
      2. 在MSDN中查找_snprintf()snprintf_s()等等。我通过谷歌的网站找到它:microsoft.com snprintf&#39; (当我需要知道MSVC做什么时,对于各种拼写&#39; snprintf&#39;)。
      3. 如遇紧急情况,请使用sprintf();缓冲区的大小足够大,不应该有任何溢出的风险,这是snprintf()等人所保护的。


          

        顺便说一句,在我的程序中有一个名为cernel_matrix(double **M1 ,double **M2)的函数,一个带有两个二维矩阵的函数。我正在将测试示例和xx传递给此函数,有时xxxx,有时test_sampletest_sample,这取决于我是否可以&#t; t使test_sample 1维;它只是功能的运作方式。否则我会收到此错误:cannot convert from 'double*' to 'double **'。我希望我解释了为什么测试样品不能是一维的。

        1. cernel_matrix()函数没有告诉矩阵有多大,所以我不知道它是如何可靠地工作的。
        2. 我不相信将test_sample传递给cernel_matrix是安全的; double matrix[][1]值不会转换为double **。因此,我不相信我理解为什么test_sample就是这样的矩阵。
        3. 我为此整理了一个微测试案例:

          extern void cernel_matrix(double **M1, double **M2);
          
          extern void m(void);
          
          void m(void)
          {
              double **m0;
              double *m1[13];
              double m2[234][1];
          
              cernel_matrix(m0, m1);
              cernel_matrix(m1, m2);
          }
          

          编译告诉我:

          x.c: In function ‘m’:
          x.c:12:5: warning: passing argument 2 of ‘cernel_matrix’ from incompatible pointer type [enabled by default]
          x.c:1:13: note: expected ‘double **’ but argument is of type ‘double (*)[1]’
          x.c:11:18: warning: ‘m0’ is used uninitialized in this function [-Wuninitialized]
          

          &#39; uninitialize&#39;警告完全有效,但问题是另一个警告及其注意事项。您应该从编译器中获得类似的东西。


            

          我认为我理解它的功能和功能,但仍然有许多我在代码中无法理解的东西。我应该能够表达所有的观点,因为我向老师做了一个演示。

          当其他人为您提供代码而因为您没有显示任何内容时,您可能会有不理解他们所做的事情的风险。

          由于您需要了解将代码呈现给教师的代码,因此您可能需要进行一些编程练习。请注意,我做的第一件事就是将问题减少到玩具尺寸(而不是2065,我用了5或10或20)。你也应该这样做。从仅包含固定大小元素的结构开始 - idt3kernel_partest_sample。使它可以初始化,导出和导入。您可以导入与导出的变量不同的变量,然后对两个变量进行比较。您甚至可以在第一个版本中省略test_sample

          如果您正常工作,请添加一个阵列及其维度成员。现在开始工作(尺寸为4x5或类似)。然后添加另一个数组(它应该是微不足道的)。当你这样做时,你应该看到我给出的例子中的各种功能,以及他们为什么会这样做。他们都是必要的&#39;在某种程度上。正如我在评论中提到的那样,我花了好几个(太多)尝试才能做到正确。我正在使用严格的警告选项进行编译,但仍然valgrind对未初始化的数据感到不安(因为我即将发布)。但我最终发现了一个未完全编辑的副本粘贴代码。

          请注意,如果您发布的代码执行了尝试导出数据的合理工作,并且最好是尝试导入数据的合理工作,那么该代码可能已得到修复。由于您没有发布任何任何值得的代码,因此很难生成解决您的真实问题的代码,而不会产生可测试的东西。我提供的代码是可测试的。测试可能更全面 - 是的,毫无疑问。但是,使代码可测试并进行测试是学习编程的重要部分。

          顺便提一下,任何类型(如数组)的可变长度数据的导出过程中的关键点是确保在写入数据(数组)本身之前写入数据(数组)的大小。然后导入过程知道在重新读取数据(数组)之前要分配多少空间。