参考:https://github.com/dmendel/bindata/wiki
BinData提供了一种声明性方式来读取和写入结构化二进制数据。
这意味着程序员指定二进制数据的格式,BinData设计出如何以这种格式读写数据。 这是一个更容易(和更可读)的替代ruby的#pack和#unpack方法。
导航
安装
您可以通过rubygems安装BinData。
gem install bindata
或者签出代码:
git clone https://github.com/dmendel/bindata.git
概述
BinData声明很容易阅读。 这里有一个例子。
class MyFancyFormat < BinData::Record
stringz :comment
uint8 :len
array :data, :type => :int32be, :initial_length => :len
end
这种花哨的格式描述以下数据集合:
:comment:一个零终止的字符串
:len:无符号8位整数
:data:无符号32位高端整数序列。 整数的数量由以下值表示:len
BinData声明与英语描述紧密匹配。 将上述声明与等效的#unpack代码进行比较,以读取这样的数据记录。
def read_fancy_format(io)
comment, len, rest = io.read.unpack("ZCa")
data = rest.unpack("N#{len}")
{:comment => comment, :len => len, :data => *data}
end
BinData声明清楚地显示了记录的结构。 #unpack代码使此结构不透明。
BinData的一般用法是将数据的结构化集合声明为用户定义的记录。 该记录可以被实例化,读取,写入和操纵,而用户不必关心基本的二进制数据表示。
记录
BinData记录声明的一般格式是包含一个或多个字段的类。
class MyName < BinData::Record
type field_name, :param1 => "foo", :param2 => bar, ...
...
end
type :是提供的类型的名称(例如uint32be,string,array)或用户定义的类型。 对于用户定义的类型,类名称从CamelCase转换为lowercased underscore_style
field_name:是您可以访问该字段的名称。 使用符号作为名称。 如果省略名称,则此特定字段为匿名。 匿名字段仍在读取和写入,但不会出现在#snapshot中。
每个字段可以具有用于如何处理数据的可选参数。 参数作为带有符号的哈希键传递。 参数被设计为懒惰评估,可能多次。 这意味着任何参数值都不能有副作用。
以下是参数的合法值的一些示例。
:param => 5
:param => lambda {foo + 2}
:param =>:bar
最简单的情况是值为字面值,例如5。
如果值不是文字,它应该是一个lambda。 将在父级的上下文中评估lambda。 在这种情况下,父项是MyName的实例。
如果值是一个符号,它将作为包含符号值的lambda的语法糖。 例如:param =>:bar等效于:param => lambda {bar}
指定默认字节序
数字类型的字节顺序必须明确定义,以便生成的代码独立于架构。 但是,为每个数字字段显式指定endian可能导致难以阅读的膨胀声明。
class A < BinData::Record
int16be :a
int32be :b
int16le :c # <-- Note little endian!
int32be :d
float_be :e
array :f, :type => :uint32be
end
endian关键字可用于设置默认字节序。 这使得声明更容易阅读。 不使用默认边框的任何数字字段可以显式覆盖它。
class A < BinData::Record
endian :big
int16 :a
int32 :b
int16le :c # <-- Note how this little endian now stands out
int32 :d
float :e
array :f, :type => :uint32
end
通过上述示例可以看出清晰度的增加。 endian关键字将级联到嵌套类型,如上例中的数组所示。
基本类型
Endian with custom types
endian关键字也可以用于标识具有字节顺序的自定义类型。 为此,自定义类型的类名必须以Le结尾,对于小尾数,Be为大尾。
class CoordLe < BinData::Record
endian :little
int16 :x
int16 :y
end
class CoordBe < BinData::Record
endian :big
int16 :x
int16 :y
end
class Rectangle < BinData::Record
endian :little
coord :upper_left # <-- Here CoordLe is automatically
coord :lower_right # <-- assumed
end
声明两个:big和:little endian自定义类型
复合类型
常见操作
高级主题
高级I / O
常问问题
如何使用字符串编码与BinData?
备择方案
这一部分纯属历史。 BinData的所有替代方法不再被积极维护。
BinData有几种替代方法。下面是BinData和其替代品之间的比较。
简短的形式是,BinData是大多数情况下的最佳选择。它是所有替代品中最全面的。它也可以说是最可读和最简单的方法来解析和写入二进制数据。
BitStruct
BitStruct是所有替代品中最完整的。它是声明性的,并支持大多数与BinData相同的原始类型。它的特殊功能是报告生成的自我记录功能。 BitStruct的设计选择是偏好速度超过灵活性。
BitStruct的主要限制是它不支持可变长度字段和依赖字段。这使得它很难处理任何非平凡的文件格式。
如果速度很重要,你只处理简单的二进制数据类型,那么BitStruct可能是一个不错的选择。对于非平凡数据类型,BinData是更好的选择。
二进制稀疏
BinaryParse是一个声明式样的打包器/解包器。它提供了与Ruby的#pack相同的原语,增加了日期和时间。像BitStruct,它不提供依赖或可变长度字段。
BinStruct
BinStruct是解包二进制数据的命令式方法。它提供了一些声明性语法糖。它支持最常见的基本类型,以及任意长度的位域。
它的主要焦点是作为一个二进制模糊器,而不是一个通用的解码/编码库。
可包装
Packable使它更好地使用Ruby的#pack和#unpack方法。而不是必须记住,例如“n”是打包16位大端整数的代码,可压缩提供了许多方便的快捷方式。在“n”的情况下,可以使用{:bytes => 2,:endian =>:big}。
使用Packable提高了#pack和#unpack方法的可读性,但显式调用#pack和#unpack并不像声明方法那样可读。
Bitpack
Bitpack提供从八位字节流中提取任意位长的大端整数的方法。
提取代码是用C编写的,所以如果速度很重要,位操作是所有你需要的功能,那么这可能是一个替代方法。这一节纯粹是历史的。 BinData的所有替代方法不再被积极维护。
BinData有几种替代方法。下面是BinData和其替代品之间的比较。
简短的形式是BinData是大多数情况下的最佳选择。它是所有替代品中最全面的。它也可以说是最可读和最简单的方法来解析和写入二进制数据。
BitStruct
BitStruct是所有替代品中最完整的。它是声明性的,并支持大多数与BinData相同的原始类型。它的特殊功能是报告生成的自我记录功能。 BitStruct的设计选择是偏好速度超过灵活性。
BitStruct的主要限制是它不支持可变长度字段和依赖字段。这使得它很难处理任何非平凡的文件格式。
如果速度很重要,你只处理简单的二进制数据类型,那么BitStruct可能是一个不错的选择。对于非平凡数据类型,BinData是更好的选择。
二进制稀疏
BinaryParse是一个声明式样的打包器/解包器。它提供了与Ruby的#pack相同的原语,增加了日期和时间。像BitStruct,它不提供依赖或可变长度字段。
BinStruct
BinStruct是解包二进制数据的命令式方法。它提供了一些声明性语法糖。它支持最常见的基本类型,以及任意长度的位域。
它的主要焦点是作为一个二进制模糊器,而不是一个通用的解码/编码库。
可包装
Packable使它更好地使用Ruby的#pack和#unpack方法。而不是必须记住,例如“n”是打包16位大端整数的代码,可压缩提供了许多方便的快捷方式。在“n”的情况下,可以使用{:bytes => 2,:endian =>:big}。
使用Packable提高了#pack和#unpack方法的可读性,但显式调用#pack和#unpack并不像声明方法那样可读。
Bitpack
Bitpack提供从八位字节流中提取任意位长的大端整数的方法。
提取代码用C编写,因此如果速度很重要,位操作是所有你需要的功能,那么这可能是一个替代。
它是什么?
你有没有发现自己写这样的代码?
io = File.open(...)
len = io.read(2).unpack("v")[0]
name = io.read(len)
width, height = io.read(8).unpack("VV")
puts "Rectangle #{name} is #{width} x #{height}"
这是丑陋,违反DRY,感觉像你在写Perl,而不是Ruby。
有一个更好的方法。
class Rectangle < BinData::Record
endian :little
uint16 :len
string :name, :read_length => :len
uint32 :width
uint32 :height
end
io = File.open(...)
r = Rectangle.read(io)
puts "Rectangle #{r.name} is #{r.width} x #{r.height}"
BinData使您可以轻松地指定要操作的数据的结构。
它支持在结构化二进制数据中找到的所有常见数据类型。 支持依赖和可变长度字段。BinData使您可以轻松地指定要操作的数据的结构。
它支持在结构化二进制数据中找到的所有常见数据类型。 支持依赖和可变长度字段。