使用环境
前一篇在介绍 JavaStruct 类时指定了使用库使用环境为 Java 5 及以上,也即开发我们使用的 JDK 版本为1.5及以上就可以了。以下讲解的用例可以直接将 code 直接粘贴到 java 的 main 函数中执行就可以了,后面会给出测试用例和结果。
使用方法
JavaStruct 类用于打包和解包结构体,也即使用方法为用该类的 pack 与 unpack 方法将定义的 struct 类转换为字节流,或者将接收的字节流转换为我们定义的 struct 类。如下所示为一个简单的用于检查结构体类的单元测试方法。结构体成员变量前有一个排序数值,也即注解方式为 @StructField(order = 0) 这是因为 Java JVM 规范没有任何有关类成员排序的说明。使用此方式定义的结构体成员会按照具体实现中使用的order进行成员内存排序,因此每一个结构体成员必须提供一个 order 数值。如下所示:
@StructClass public class Foo {@StructField(order = 0)public byte b;@StructField(order = 1)public int i;}
注意,注解 @StructClass 以及 @StructField 不能省略。结构体定义完成后,使用 pack 与 unpack 方法进行类型转换,如下所示为完整示例:
public class test {@StructClass public class Foo {@StructField(order = 0)public byte b;@StructField(order = 1)public int i;}public void TestFoo() {try { // Pack the class as a byte buffer Foo f = new Foo();f.b = (byte)1;f.i = 2; byte[] b = JavaStruct.pack(f);for (int i = 0; i < b.length; i++) {System.out.printf("b[%d]: %d\n", i, b[i]);}// Unpack it into an objectFoo f2 = new Foo();JavaStruct.unpack(f2, b);System.out.println("f2.b: " + f2.b);System.out.println("f2.i: " + f2.i);} catch(StructException e) { e.printStackTrace();} }public static void main(String args[]) {test t = new test();t.TestFoo();}
}
直接观察输出结果:
从输出结果可以看到,我们定义的结构体被转换成了 5 个字节的 byte 数组(int 占 4 个字节),可以看出来 int 数据的地字节保存在了 byte 数组的高地址,可见使用 pack 打包时为大端排序。当然,实际应用时我们需要根据需求决定是使用大端还是小端排序。在 pack 与 unpack 方法指定就可以了,具体 pack 默认为大端还是小端排序和处理器架构及编译器版本都有关系,因此要在项目应用中以真实结果为准。如改成小端:
byte[] b = JavaStruct.pack(f, ByteOrder.LITTLE_ENDIAN);
如果运行中发生错误,结构体操作会抛出 StructException 异常。
Struct 类也可以直接与 Stream 流一起使用。可以参考 Photoshop ACB 文件读取 example,这里就不作详细分析了。片段如下:
public void TestACB() {public void read(String acbFile) { try { FileInputStream fis = new FileInputStream(new File(acbFile)); header = new ACBHeader(); StructUnpacker up = JavaStruct.getUnpacker(fis, ByteOrder.BIG_ENDIAN); up.readObject(header); }}}
原型相关
对于使用原型,要注意对于 private 与 protected 成员需要用相应的getter 与 setter 方法。Transient 成员会被自动排除。如下所示:
@StructClass public class PublicPrimitives implements Serializable { @StructField(order = 0) public byte b;@StructField(order = 1)public char c;@StructField(order = 2)public short s;@StructField(order = 3)public int i;@StructField(order = 4)public long lo;@StructField(order = 5)protected float f;@StructField(order = 6)private double d;transient int blah;transient double foo;public float getF() {return f;}public void setF(float f) {this.f = f;}public double getD() {return d;}public void setD(double d) {this.d = d;}public boolean equals(Object o){PublicPrimitives other = (PublicPrimitives)o;return (this.b == other.b && this.c == other.c&& this.s == other.s&& this.i == other.i&& this.lo == other.lo&& this.f == other.f&& this.d == other.d);}}
数组相关
使用数组有一些先决条件。当解包时,数组一定要分配充足的空间。只有那些在另一个字段中使用ArrayLengthMarker(见下文) 定义长度的数组可以为 null,这些数组在解包时会自动分配空间。除此之外的数组定义不能为空和未初始化状态。使用如下所示:
@StructClass public class PublicPrimitiveArrays { @StructField(order = 0) public byte[] b = new byte[5];@StructField(order = 1)public char[] c = new char[10];@StructField(order = 2)public short[] s;@StructField(order = 3)public int[] i;}
数组长度标记相关
数组长度标记(Array Length Markers)对于长度在另一个字段中定义的字段十分有用。参见以下示例,这是个特殊的字符串结构体,其有一个长度字段以及紧跟其后的对应这个长度的 16 位字符。也即结构为:
| Length | UTF-16 Characters ... |
为了处理这种情况,必须把这些字符串表示为一个特殊的结构体类。长度字段应该注解为“ArrayLengthMarker”。通过这种方式,javastruct 可以在打包及解包操作中操作数组字段时自动使用长度字段中的值。示例如下:
@StructClasspublic class AString {@StructField (order = 0 )@ArrayLengthMarker (fieldName = "chars")public int length;@StructField (order = 1)public char[] chars;public AString(String content){this.length = content.length();this.chars = content.toCharArray();}}
关于 JavaStruct 应用的文章系列,可以移步至如下链接:
1. 《Java 结构体之 JavaStruct 使用教程<一> 初识 JavaStruct》
2. 《Java 结构体之 JavaStruct 使用教程<二> JavaStruct 用例分析》
3. 《Java 结构体之 JavaStruct 使用教程<三> JavaStruct 数组进阶》
下载地址:http://download.csdn.net/download/jazzsoldier/9905451
有任何疑问或使用问题可以给我评论或者邮件哦,觉得有用就点赞吧~:-D