我有一个这样的结构
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
答案 0 :(得分:1)
对于如此复杂的结构,它是一项适度的重大任务。这是一个不那么短的SSCCE(Short, Self-Contained, Complete Example)。真的有3个文件撞到了一个:
stderr.h
- 错误报告功能的声明(前10行)serialize.c
- 序列化代码(中间不到300行)stderr.c
- 错误报告功能(底部40行)我不打算解释错误报告功能。就格式化参数而言,它们或多或少地像printf()
一样工作,但是它们写入标准错误,而不是标准输出,它们包括程序名称作为前缀,错误来自errno
。 emalloc()
函数检查内存分配,报告错误并在分配失败时退出。这种错误处理适用于简单的程序;对于需要恢复的复杂程序来说,如果出现内存问题,保存工作等等,这是不够的。
在真正的序列化代码中,有4组函数,加上main()
来协调。
打印功能允许人们查看数据,您可以将输出保存到文件,并将导出数据与导入数据进行比较,以确保它们相同。
如果使用结构来描述所有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 *
;在开始将这些问题嵌入原始程序之前,如何解决这些问题呢?
或者,因为您可能在Windows上并使用MSVC:
double **data = (double **)emalloc(rows * sizeof(*data));
_snprintf()
和snprintf_s()
等等。我通过谷歌的网站找到它:microsoft.com snprintf&#39; (当我需要知道MSVC做什么时,对于各种拼写&#39; snprintf&#39;)。如遇紧急情况,请使用sprintf()
;缓冲区的大小足够大,不应该有任何溢出的风险,这是snprintf()
等人所保护的。
顺便说一句,在我的程序中有一个名为
cernel_matrix(double **M1 ,double **M2)
的函数,一个带有两个二维矩阵的函数。我正在将测试示例和xx
传递给此函数,有时xx
和xx
,有时test_sample
和test_sample
,这取决于我是否可以&#t; t使test_sample
1维;它只是功能的运作方式。否则我会收到此错误:cannot convert from 'double*' to 'double **'
。我希望我解释了为什么测试样品不能是一维的。
cernel_matrix()
函数没有告诉矩阵有多大,所以我不知道它是如何可靠地工作的。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);
}
编译告诉我:
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)。你也应该这样做。从仅包含固定大小元素的结构开始 - id
,t3
,kernel_par
和test_sample
。使它可以初始化,导出和导入。您可以导入与导出的变量不同的变量,然后对两个变量进行比较。您甚至可以在第一个版本中省略test_sample
。
如果您正常工作,请添加一个阵列及其维度成员。现在开始工作(尺寸为4x5或类似)。然后添加另一个数组(它应该是微不足道的)。当你这样做时,你应该看到我给出的例子中的各种功能,以及他们为什么会这样做。他们都是必要的&#39;在某种程度上。正如我在评论中提到的那样,我花了好几个(太多)尝试才能做到正确。我正在使用严格的警告选项进行编译,但仍然valgrind
对未初始化的数据感到不安(因为我即将发布)。但我最终发现了一个未完全编辑的副本粘贴代码。
请注意,如果您发布的代码执行了尝试导出数据的合理工作,并且最好是尝试导入数据的合理工作,那么该代码可能已得到修复。由于您没有发布任何任何值得的代码,因此很难生成解决您的真实问题的代码,而不会产生可测试的东西。我提供的代码是可测试的。测试可能更全面 - 是的,毫无疑问。但是,使代码可测试并进行测试是学习编程的重要部分。
顺便提一下,任何类型(如数组)的可变长度数据的导出过程中的关键点是确保在写入数据(数组)本身之前写入数据(数组)的大小。然后导入过程知道在重新读取数据(数组)之前要分配多少空间。