我最近使用C/C++加载了很多二进制文件,我被它是多么的不优雅所困扰。要么我得到了很多看起来像这样的代码(我已经离开了):
uint32_t type, k;
uint32_t *variable;
FILE *f;
if (!fread(&type, 4, 1, f))
goto boundsError;
if (!fread(&k, 4, 1, f))
goto boundsError;
variable = malloc(4 * k);
if (!fread(variable, 4 * k, 1, f))
goto boundsError;或者,我定义了一个本地的压缩结构,这样我就可以更容易地读取固定大小的块。然而,在我看来,对于这样一个简单的问题-即将指定的文件读入内存-可以更有效、更具可读性的方式完成。有没有人有什么小窍门/窍门?我想澄清的是,我不是在寻找一个库或其他东西来处理这个问题;如果我正在设计自己的文件,并且必须对文件规范进行大量更改,我可能会受到诱惑,但现在我只是在寻找风格上的答案。
此外,你们中的一些人可能会建议mmap-I喜欢mmap!我经常使用它,但它的问题是它导致了处理未对齐数据类型的讨厌代码,这在使用stdio时并不真正存在。最后,我将编写类似于stdio的包装器函数,以便从内存中读取数据。
谢谢!
编辑:我还应该澄清我不能改变文件格式--有一个我必须读取的二进制文件;我不能请求另一种格式的数据。
发布于 2010-11-14 10:20:07
如果要反序列化二进制数据,一种选择是为要使用的结构定义序列化宏。在使用模板函数和流的C++中,这要容易得多。(boost::serialization是一个非侵入式的序列化库,但如果您想要进行侵入式的序列化,您可以让它更优雅)
简单的C语言宏:
#define INT(f,v) \
{ int _t; fread(&_t, sizeof(int), 1, f); v = ntohl(_t); }
#define FLOAT(f,v) \
{ int _t; fread(&_t, sizeof(int), 1, f); v = ntohl(_t); /* type punning */ memcpy(&v, &_t, sizeof(float)); }
...用法:
int a;
float b;
FILE *f = fopen("file", "rb");
INT(f, a);
FLOAT(f, b);是的,序列化代码是编写起来最无聊、最愚蠢的代码之一。如果可以,使用元数据描述您的数据结构,并机械地生成代码。有一些工具和库可以帮助实现这一点,您也可以使用Perl、Python、PowerShell或其他语言编写自己的库。
发布于 2010-11-14 10:00:53
对于这个问题,我所见过的最优雅的解决方案是Sean Barrett的writefv,它用在他的小型图像写入库stb_image_write available here中。他只实现了几个原语(没有错误处理),但同样的方法可以扩展到基本上是二进制printf (对于读取,您可以执行相同的操作来获得二进制scanf)。非常优雅和整洁!事实上,整个事情是如此简单,我不妨把它包含在这里:
static void writefv(FILE *f, const char *fmt, va_list v)
{
while (*fmt) {
switch (*fmt++) {
case ' ': break;
case '1': { unsigned char x = (unsigned char) va_arg(v, int); fputc(x,f); break; }
case '2': { int x = va_arg(v,int); unsigned char b[2];
b[0] = (unsigned char) x; b[1] = (unsigned char) (x>>8);
fwrite(b,2,1,f); break; }
case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4];
b[0]=(unsigned char)x; b[1]=(unsigned char)(x>>8);
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
fwrite(b,4,1,f); break; }
default:
assert(0);
return;
}
}
}下面是他如何使用它来编写真彩色.BMP文件:
static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, const char *fmt, ...)
{
FILE *f;
if (y < 0 || x < 0) return 0;
f = fopen(filename, "wb");
if (f) {
va_list v;
va_start(v, fmt);
writefv(f, fmt, v);
va_end(v);
write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad);
fclose(f);
}
return f != NULL;
}
int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)
{
int pad = (-x*3) & 3;
return outfile(filename,-1,-1,x,y,comp,(void *) data,0,pad,
"11 4 22 4" "4 44 22 444444",
'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header
40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header
}(省略了write_pixels的定义,因为它在这里非常切线)
发布于 2010-11-14 10:20:00
我可以通过对代码进行一些重构,让它看起来不那么难看,这样复杂的数据结构就可以通过对其底层类型的一系列调用来读取。
我假设您的代码是纯C语言,而不是C++,因为在后者中,您可能会抛出异常,而不是使用goto语句。
https://stackoverflow.com/questions/4175754
复制相似问题