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

我有这样的结构

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

对于这样一个复杂的结构,这是一项谦虚的重大任务。 这是一个不那么短的SSCCE( 短,自包含,完整的例子 )。 真的有3个文件撞到一个:

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

我不打算解释错误报告function。 就格式化参数而言,它们或多或少像printf()一样工作,但它们写入标准错误,而不是标准输出,它们包括程序名称作为前缀,以及从errno派生的errnoemalloc()函数检查内存分配,报告错误并在分配失败时退出。 这种error handling适用于简单的程序; 如果存在内存问题,节省工作或其他任何事情需要恢复的复杂程序,这是不够的。

在真正的序列化代码中,有4组函数,还有main()到orchestrate。

  1. 分配和初始化函数用于创建和初始化结构。
  2. 打印function以转储结构。
  3. 导出函数以序列化数据以进行导出。
  4. 导入函数以反序列化要导入的数据。

打印function允许人员查看数据,您可以将输出保存到文件,并将导出数据与导入数据进行比较,以确保它们是相同的。

如果使用结构来描述所有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][1];double test_sample[2065]; 。 我会观察它会使代码比其他方式更复杂。 我最终通过使用&data->test_sample[0][0]作为起点将其视为正常的一维double数组。

进行序列化的方法不止一种。 我已经选择了由N个1D数组表示的二维2D数组,每个1D数组的前缀是一个描述1D数组大小的size_t 。 这在文件中提供了一些冗余,这意味着错误检测稍微好一些。 简单地输出2D数组的两个维度,然后输出行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  #include  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  #include  #include  #include  #include  static char const *arg0 = ""; 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编译器更新到系统。

或者,因为您可能在Windows上并使用MSVC:

  1. 使用cast double **data = (double **)emalloc(rows * sizeof(*data));
  2. 在MSDN中查找_snprintf()snprintf_s()等等。 当我需要知道MSVC做什么时,我通过Google找到了’site:microsoft.com snprintf’(针对’snprintf’的各种拼写)。

在紧急情况下,使用sprintf() ; 缓冲区的大小足够大,不应该有任何溢出的风险,这是snprintf()等人所预防的。


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

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

我为此整理了一个微测试用例:

 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); } 

编译告诉我:

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

“未初始化”警告完全有效,但问题是另一个警告及其注释。 您应该从编译器中获得类似的东西。


我想我理解它和function的想法,但仍然有很多我在代码中不理解的东西。 我应该能够表达所有的观点,因为我向老师介绍了一下。

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

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

当你有了这个工作,然后添加一个数组及其维度成员。 现在开始工作(尺寸为4×5或类似)。 然后添加另一个数组(它应该是微不足道的)。 当你这样做时,你应该看到我给出的示例中的各种函数,以及它们为什么存在。 他们在某种程度上都是“必要的”。 正如我在评论中提到的那样,我花了好几个(太多)尝试才能做到正确。 我正在使用严格的警告选项进行编译,但是valgrind仍然在考虑未初始化的数据(因为我即将发布)。 但我最终发现了一条未完全编辑的copy’n’paste代码。

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

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