使用Java读取结构化二进制文件的最佳方法
我必须用Java读取传统格式的二进制文件。
简而言之,这个文件有一个由多个整数,字节和固定长度的char数组组成的头文件,后面跟着一个由整数和字符组成的logging列表。
在任何其他语言中,我将创buildstruct
s(C / C ++)或record
s(Pascal / Delphi),它们是标头和logging的逐字节表示。 然后,我将sizeof(header)
字节读入一个头部variables,并为logging做同样的事情。
像这样的东西:(delphi)
type THeader = record Version: Integer; Type: Byte; BeginOfData: Integer; ID: array[0..15] of Char; end; ... procedure ReadData(S: TStream); var Header: THeader; begin S.ReadBuffer(Header, SizeOf(THeader)); ... end;
用Java做类似的最好的方法是什么? 我是否必须自己阅读每一个价值,还是有其他的方式来做这种“块阅读”?
据我所知,Java迫使你读取一个文件作为字节,而不是能够阻止读取。 如果你正在序列化Java对象,这将是一个不同的故事。
显示的其他示例使用DataInputStream类与File,但也可以使用快捷方式: RandomAccessFile类:
RandomAccessFile in = new RandomAccessFile("filename", "r"); int version = in.readInt(); byte type = in.readByte(); int beginOfData = in.readInt(); byte[] tempId; in.read(tempId, 0, 16); String id = new String(tempId);
请注意,您可以将响应对象转换为类,如果这样做会更容易。
您可以使用DataInputStream类,如下所示:
DataInputStream in = new DataInputStream(new BufferedInputStream( new FileInputStream("filename"))); int x = in.readInt(); double y = in.readDouble(); etc.
一旦你得到这些价值观,你可以随心所欲地做。 在API中查找java.io.DataInputStream类以获取更多信息。
如果你要使用Preon ,那么你只需要这样做:
public class Header { @BoundNumber int version; @BoundNumber byte type; @BoundNumber int beginOfData; @BoundString(size="15") String id; }
一旦你有了这个,你使用一行创build编解码器:
Codec<Header> codec = Codecs.create(Header.class);
你使用这样的编解码器:
Header header = Codecs.decode(codec, file);
我可能误解了你,但是在我看来,你正在创build内存结构,你希望能够以每字节为单位精确地表示你想从硬盘读取的内容,然后将整个内容复制到内存中,操纵?
如果确实如此,那么你正在玩一个非常危险的游戏。 至less在C语言中,这个标准并没有强制填充或者alignment一个结构体的成员。 更不用说大/小sorting或奇偶校验位了……所以,即使你的代码碰巧运行,它也是非常不便携和风险的 – 你依赖于编译器的创build者在未来的版本中不会改变主意。
最好创build一个自动机来validation从HD读取的结构(每个字节的字节)是否有效,如果确实没问题,则填充内存中的结构。 尽pipe你获得了平台和编译器的独立性,但是你可能会丢失一些毫秒(而不是现代操作系统看起来很多的磁盘读取caching)。 另外,你的代码将被轻松移植到另一种语言。
post编辑:以某种方式,我同情你。 在DOS / Win3.11的良好日子里,我曾经创build了一个C程序来读取BMP文件。 并使用完全相同的技术。 一切都很好,直到我试图编译它的Windows – 哎呀! Int现在是32位长,而不是16位! 当我尝试在Linux上编译时,发现gcc与Microsoft C(6.0!)有不同的位域分配规则。 我不得不诉诸macros伎俩,使其便携式…
我使用Javolution和javastruct,都处理字节和对象之间的转换。
Javolution提供了表示Ctypes的类。 所有你需要做的是编写一个描述C结构的类。 例如,从C头文件中,
struct Date { unsigned short year; unsigned byte month; unsigned byte day; };
应该被翻译成:
public static class Date extends Struct { public final Unsigned16 year = new Unsigned16(); public final Unsigned8 month = new Unsigned8(); public final Unsigned8 day = new Unsigned8(); }
然后调用setByteBuffer
来初始化对象:
Date date = new Date(); date.setByteBuffer(ByteBuffer.wrap(bytes), 0);
javastruct使用注释来定义C结构中的字段。
@StructClass public class Foo{ @StructField(order = 0) public byte b; @StructField(order = 1) public int i; }
要初始化一个对象:
Foo f2 = new Foo(); JavaStruct.unpack(f2, b);
我猜FileInputStream让你读取字节。 所以,用FileInputStream打开文件并读入sizeof(header)。 我假设头有一个固定的格式和大小。 我没有看到在初始文章中提到的,但假设情况是如此,如果头部具有可选的参数和不同的尺寸,将会变得更复杂。
一旦你有了信息,就可以有一个头文件类,你可以在其中分配你已经读过的缓冲区的内容。 然后以类似的方式parsinglogging。
这是一个使用ByteBuffer(Java NIO)读取字节的链接,
正如其他人所说,DataInputStream和Buffers可能是您在处理java中的二进制数据之后的低级API。
不过,你可能需要像构造 (维基页面也有很好的例子: http : //en.wikipedia.org/wiki/Construct_(python_library) ,但对于Java。
我不知道任何(Java版本),但采取这种方法(声明性地指定在代码中的结构)可能是正确的路要走。 在Java中使用合适的stream畅接口可能与DSL非常相似。
编辑:一点谷歌search显示:
http://javolution.org/api/javolution/io/Struct.html
这可能是你正在寻找的东西。 我不知道它是有效的还是好的,但它看起来是一个明智的开始。
我将创build一个包装数据的ByteBuffer表示的对象,并提供getter直接从缓冲区中读取。 这样,您可以避免将数据从缓冲区复制到原始types。 此外,您可以使用MappedByteBuffer来获取字节缓冲区。 如果你的二进制数据是复杂的,你可以使用类来build模,并为每个类分配一个缓冲区版本。
class SomeHeader { private final ByteBuffer buf; SomeHeader( ByteBuffer fileBuffer){ // you may need to set limits accordingly before // fileBuffer.limit(...) this.buf = fileBuffer.slice(); // you may need to skip the sliced region // fileBuffer.position(endPos) } public short getVersion(){ return buf.getShort(POSITION_OF_VERSION_IN_BUFFER); } }
从字节缓冲区读取无符号值的方法也是有用的。
HTH
我已经写了一个技术来做这种事情在Java中 – 类似于旧的C类阅读位字段的成语。 请注意,这只是一个开始,但可以扩展。
这里
在过去,我使用DataInputStream以指定的顺序读取任意types的数据。 这不会让你轻易解释big-endian / little-endian的问题。
从1.4版本开始,java.nio.Buffer系列可能是要走的路,但是看起来你的代码实际上可能更复杂。 这些类确实支持处理endian问题。
前一段时间,我发现这篇文章使用reflection和parsing来读取二进制数据。 在这种情况下,作者使用reflection来读取java二进制.class文件。 但是,如果您正在将数据读入类文件,可能会有所帮助。