Java 结构体之 JavaStruct 使用教程<二> JavaStruct 用例分析

使用环境

前一篇在介绍 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 object
			Foo 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 成员需要用相应的gettersetter 方法。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 可以在打包及解包操作中操作数组字段时自动使用长度字段中的值。示例如下:

	@StructClass
	public 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


猜你喜欢

转载自blog.csdn.net/jazzsoldier/article/details/75633520
今日推荐