设计二进制文件格式

  • 前言
    • 1、为何需要一种二进制的文件格式
    • 2、文件格式的具体设计
      • (1)整体的文件结构
      • (2)文件头魔数(magic number)
      • (3)检验码
      • (4)版本号
      • (5)字节顺序
      • (6)字节对齐
      • (7)回写和流写

前言

本文是由于需要设计一种二进制的文件格式用于保存前文中所提取出来的wav文件的采样数据故而写下本篇文章。

1、为何需要一种二进制的文件格式

程序时常需要保存自身的文档数据。比如一个矢量绘图程序,需要将用户绘制的每个图元都保存到文件中,以后再次打开。应该优先考虑文本格式,文本格式容易测试和编辑。更应该优先考虑通用的文本格式,比如 XML, JSON, Lua 等等。这些通用的文本格式已经存在大量的工具和库,可以省下很多功夫。

文本格式读取慢,并且文件尺寸也比较大(就算经过 zip 压缩),大多数情况下这都不是什么问题。但一些场合,要求更快读取速度,更小文件尺寸,这时就需要自己来设计一种二进制文件格式。游戏中的模型数据,就要求读取速度快;而经常通过网络传输的文件,就要求减少文件尺寸,比如 swf 格式。

2、文件格式的具体设计

具体的二进制文件格式,要根据具体的程序需求来设计。但有些设计思路,是所有二进制格式都通用的。了解这些,对将来分析其它的二进制格式也会有帮助。

(1)整体的文件结构

常见二进制文件格式,时常采用 文件头 + 分区 的结构:
file header
section 0
section 1
section 2
section 3

section N

1)文件头(file header ):描述了文件的整体信息,常见的字段有魔数、版本号、检验码、文件大小等等。文件头根据文件的具体用途会有额外的字段,比如一张图片,文件头当中就可以含有表示图片尺寸的字段。

2)分区(section):其结构通常是标签+长度+分区数据
tag + length
section data
tag 和 length 合起来是分区头部,后面紧跟着分区的具体数据。

tag:可以是一个整数,也可以是一个字符串。tag 用来标识分区,不同的 tag 表示不同的分区种类,不同的分区种类有各自不同的读取方式。
比如:
#define kPicShapeTag 1
#define kPreivewTag 2
当 tag 为 1 时,就表示是这个分区存放的是图元数据,当为 2 是表示这个分区存放一张预览图。

length:length 是个整数,表示分区数据的具体长度(不包括分区头部)或者表示整个分区的长度(包括分区头部)。
PS:这种分区结构使得文件格式容易扩展,有新需求时就定义一个新的分区类型,原来的文件结构不需要修改。也容易「向上兼容」。

向上兼容:新版本程序生成的新版本格式,可以使用旧版本程序打开。只是旧版本打开时,一些新功能无法应用,比如ps中新版本的滤镜功能旧版本没有会自动跳过此段二进制数据。

向下兼容:旧版本程序产生的旧版本文件格式,可以使用新版本的程序打开。应用程序升级,向下兼容是必要的。

(2)文件头魔数(magic number)

文件头当中,会有一个数字作为文件格式的标识。这个数字可以随意选定任何值,也可以占据任何字节(通常是 4 字节或者 8 字节),但这个数字选定之后就会固定下来,基本上不会再有变化。在编程领域,一些说不清来历比较任意的数字会被称呼为魔数( magic number)。因此这个随意选定用于标识文件格式的数字,就叫文件格式魔数;这个数字通常放在文件头当中,有时也就称为文件头魔数。

为了方便处理,避免数字在不同字节顺序的机器上有所区别,有时文件头魔数会定义成多字节格式,比如:

struct Header
{uint8_t md5[16]; // md5 作为检验码,uint8_t是代表一字节的数据类型char magic[8];   // 魔数
};Header header;
memcpy(header.magic, "vecpaint", 8); //拷贝vecpaint到header.magic作为魔数,共8个字节

文件头魔数无论被当成整数还是多个字节处理,它的作用都是相同的,只是作为一个文件格式的标识。

(3)检验码

文件头通常还会有个检验码,用于检验文件是否完整并且没有经过修改的。这个检验码可以使用 crc, 可以使用 md5,也可以使用其它算法。只要达到这个目的就行。

假如文件都是在本机写入和读取,这个检验码没有什么大作用。但假如文件格式经过网络传输,这个检验码就十分有用了。网络传输经常会发生数据不全,或者某些字节被改变了,导致文件数据不完整。通过这个检验码可以检测出这种问题,以便再做进一步处理(比如重新下载一次)。

用文件的剩余数据计算出它的 md5, 存放在文件最开头。这个 md5 一方面可以作为这个文件的检验码,另一方面可以作为这个文件的 key。读取文件格式的时候,先判断魔数是否正确,再重新计算出 md5 进行比较。md5 出错,表示文件不完整或者经过改动。在需要更安全的场合,md5 可能被人伪造,但平常应用基本足够了。

(4)版本号

文件头通常还会包含版本号。版本号不同的文件格式,读取方式可能会有所不同。不支持「向上兼容」的软件,碰到比它可以支持的更高版本的文件格式,就直接读取失败,并返回一个错误信息。

大部分软件的版本号采用三个数字,用小数点分隔,格式为:

主版本号.次版本号.补丁版本号

主版本号通常表示功能有很大改动,甚至界面都改掉了;次版本号用于表示添加了一些小功能;补丁版本号只是用了 fix bugs。iOS 系统的采用这种版本表示方式。

(5)字节顺序

字节顺序有大端字节序和小端字节序。不同的机器字节序有可能不同,设计文件格式时需要考虑文件用什么字节序保存数据的。不然有可能在这一台机器上生成的文件,传输到另一台机器上就打开失败了。

PS:有些文件格式,可以同时支持大端和小端字节序。它有文件头中有个字段指明文件保存的时候是采用什么方式保存。

(6)字节对齐

假设机器是 32 位(也就是 4 字节),当数据的地址为 4 的倍数时,计算机的读取速度会更快。64位以此类推。

C/C++ 编译器编译代码时,也会尽量使得数据字节对齐,比如下面结构:

struct Test
{int a;double b;int c;double d;
};

编译器为使得数据不跨越格子边界,整个结构在 64 位机上占据 32 个字节。但稍微调整一下:

struct Test
{int a;int c;double b;double d;
};

就这样交换一些数据定义顺序,整个结构在 64 位机上占据 24 个字节,比原来节省了 8 个字节。

(7)回写和流写

1)回写:是指数据写入之后,可以回头再修改。比如分区数据最通常以 tag + length 开头,但最开始是不知道最终的分区数据长度的。这样当写入分区头的 length 字段时,就只能先随意写些临时值。当写完最后的分区数据,知道数据长度了,再回头修改长度。

2)流写:比如将数据写入网络当中,不可能回头修改网络上的数据。这种一直写,不能回头修改的就叫「流写」。

有些约束条件下,二进制文件格式就需要支持「流写」,比如在网络一端生成数据,在网络另一边读取数据。这种情况下,可以将 文件头 + 分区数据 稍微调整一下变成分区数据 + 文件尾。当按顺序写完所有分区数据,也就知道文件的整体信息,就可以依次写入文件尾的各字段。

本篇文章写到此处,具体的编程实现可以继续关注我的文章。

参考文章:
二进制文件格式设计

如何设计二进制文件格式相关推荐

  1. 设计一个二进制文件格式

    NOTES 本文来源:Designing File Formats 翻译由 本人(赤石俊哉) 整理,若您是原作者并认为此文涉及版权侵犯,我会配合删除. 身份识别字符 头部验证码 版本信息 数据位移 其 ...

  2. MIDI二进制文件格式简析

    MIDI二进制文件格式简析 本文主要参考自Official MIDI Specifications Chunks 每个MIDI文件由一系列chunk组成,每个chunk的前四个字节为魔数(magic ...

  3. 微软官方知识:了解 Office 二进制文件格式

    摘要:了解有关当前和早期 Microsoft Office 产品中使用的二进制文件格式,包括如何使用它们,其基本结构和以编程方式与其交互的主要概念. 适用范围: Microsoft Word | Mi ...

  4. matlab设计二进制波形,二进制调制系统的仿真与分析

    二进制调制系统的仿真与分析 内容包括:一 设计内容与技术要求1.设计内容:对二进制数字信源进行数字调制(2ASK ) ,画出信号波形及功率谱.并分析其性能.2.技术要求①. 掌握二进制数字信号调制系统 ...

  5. mac镜像cdr格式_设计常用文件格式!萌新必备

    文/张丛 投稿 AI格式是一种矢量图形文件,适用于ADOBE公司的illustrator软件输出格式,与PSD格式文件相同,AI文件也是一种分层文件,每个对象都是独立的,它们具有各自的属性,如:大小. ...

  6. FBX二进制文件格式规范

    FBX binary file format specification FBX是一种流行的3D文件格式,最初由Kaydara为MotionBuilder开发,于2006年被Autodesk公司收购. ...

  7. python算法设计 - 二进制

    python算法设计源码:https://github.com/MakerChen66/Python3Algorithm 版权声明:原创不易,本文禁止抄袭.转载,侵权必究! 目录 一.二进制1的个数 ...

  8. 使用 010 Editor 分析二进制文件格式

    010 Editor 是一款很好用的编辑器,其中有一个很好的特点是可以运行模板文件来分析二进制文件. 官网地址是:http://www.sweetscape.com 模板文件地址是:http://ww ...

  9. C语言编写的文本文件/二进制文件格式互换

    本程序要自己创建个文本格式的输入文件a1.txt,编译后能将文本文件前255字节以内的字符转换成相应的AscII码值的二进制表示,并存入输出文件a2.txt中.然后再将二进制文件还原并存入a3.txt ...

最新文章

  1. 好久没写blog了。最近感觉自己老了
  2. 什么样的网站结构备受搜索引擎喜爱?
  3. CTFshow 爆破 web23
  4. 安徽大学计算机语言学考研真题,安徽大学外国语言学及应用语言学考研经验考研真题考研参考书复试分数线...
  5. linux 其他参数
  6. Java高级 —— 泛型
  7. 【软件质量】改善结构松散的代码
  8. 解决 pycharm can not save setting
  9. Sklearn 与 TensorFlow 机器学习实用指南(补档)
  10. java实现筑业软件官网下载,筑业资料软件2020旗舰版|筑业资料软件免费官方下载-系统族...
  11. 原生js和jquery 获取文档高度
  12. 压力传感器原理与组成
  13. 网络受限是个什么东东?
  14. 微信没有回车键怎么换行_微信打字怎么换行
  15. 1块钱30分钟利用华为云服务器配置一台云电脑并搭建一个简易网站(步步截图较详细)
  16. FiveThirtyEight Comic Characters Dataset(五分之八漫画人物数据集)
  17. 链路聚合负载分担方式
  18. 使用Python修改图片尺寸
  19. 多年Excel使用经验汇总
  20. vc不支援此种界接口_护肤圈人人在夸的“早C晚A”,做完功课后我发现:不一定适合你!...

热门文章

  1. 达梦数据库导入.dmp文件标准教程
  2. 考研高数背诵类知识点-张宇(不定期更新)
  3. 【实验】主题建模工具BERTopic的安装及使用过程中的报错解决方案
  4. matlab中求分段函数的分段点,matlab求解分段函数问题是如何用下面的算法求解下面的分段函数 爱问知识人...
  5. Kong 网关 快速入门
  6. 8 PyQt5 QTabWidget
  7. 读书笔记--电气符号(图形符号)
  8. oracle --sql 排名函数
  9. Android 新的锁屏接口的实现
  10. Linux脚本 括号,linux shell取小括号()、中括号[]、大括号{}等里的内容