首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C中优雅的二进制i/o?

C中优雅的二进制i/o?
EN

Stack Overflow用户
提问于 2010-11-14 09:48:09
回答 6查看 788关注 0票数 1

我最近使用C/C++加载了很多二进制文件,我被它是多么的不优雅所困扰。要么我得到了很多看起来像这样的代码(我已经离开了):

代码语言:javascript
运行
复制
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的包装器函数,以便从内存中读取数据。

谢谢!

编辑:我还应该澄清我不能改变文件格式--有一个我必须读取的二进制文件;我不能请求另一种格式的数据。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2010-11-14 10:20:07

如果要反序列化二进制数据,一种选择是为要使用的结构定义序列化宏。在使用模板函数和流的C++中,这要容易得多。(boost::serialization是一个非侵入式的序列化库,但如果您想要进行侵入式的序列化,您可以让它更优雅)

简单的C语言宏:

代码语言:javascript
运行
复制
#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)); }
...

用法:

代码语言:javascript
运行
复制
  int a;
  float b;
  FILE *f = fopen("file", "rb");

  INT(f, a);
  FLOAT(f, b);

是的,序列化代码是编写起来最无聊、最愚蠢的代码之一。如果可以,使用元数据描述您的数据结构,并机械地生成代码。有一些工具和库可以帮助实现这一点,您也可以使用Perl、Python、PowerShell或其他语言编写自己的库。

票数 1
EN

Stack Overflow用户

发布于 2010-11-14 10:00:53

对于这个问题,我所见过的最优雅的解决方案是Sean Barrett的writefv,它用在他的小型图像写入库stb_image_write available here中。他只实现了几个原语(没有错误处理),但同样的方法可以扩展到基本上是二进制printf (对于读取,您可以执行相同的操作来获得二进制scanf)。非常优雅和整洁!事实上,整个事情是如此简单,我不妨把它包含在这里:

代码语言:javascript
运行
复制
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文件:

代码语言:javascript
运行
复制
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的定义,因为它在这里非常切线)

票数 3
EN

Stack Overflow用户

发布于 2010-11-14 10:20:00

我可以通过对代码进行一些重构,让它看起来不那么难看,这样复杂的数据结构就可以通过对其底层类型的一系列调用来读取。

我假设您的代码是纯C语言,而不是C++,因为在后者中,您可能会抛出异常,而不是使用goto语句。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4175754

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档