Reputation: 1322
I've got a binary format file, that I want to turn into Java objects and then finally output that into CSV format.
I've started going down a route, that although I know will work, seems incorrect.
Can someone please either tell me this is the way to do this, or suggest alternatives.
Sample code below:
public class Baf5014Converter
{
//private recordSize
public Baf5014 convertBytesToObject(byte[] bafRecordInBytes) {
Baf5014 record = new Baf5014();
record.setSize(getRecordSize(bafRecordInBytes));
return record;
}
private int getRecordSize(byte[] bafRecordInBytes)
{
byte[] recordSizeInBytes = Arrays.copyOfRange(bafRecordInBytes,0,2);
return ByteBuffer.wrap(recordSizeInBytes).getShort();
}
}
The idea would be to create a number of different getFoo functions as I go through the file. What I don't particularly like already is the magic numbers 0,2 in the above, even if the function name I guess makes it obvious enough what it's doing.
Googling hasn't helped so far, but it might be that I don't know the right words to search for :)
Any help would be greatly appreciated,
Cheers
Alan
Upvotes: 0
Views: 3066
Reputation: 41223
There's no "one true" approach to this, and yours is OK. There's nothing wrong with "magic numbers" - they simply correspond to magic numbers in the file format you are reading. It's probably a good idea to make the magic numbers constants, for readability.
One approach you might take is to have a class with an InputStream as a constructor parm.
public MyObj(InputStream binaryStream) {
Scanner scanner = new Scanner(binaryStream);
this.recordSize = scanner.nextShort();
...
}
Then to create objects:
FileInputStream fis = new FileInputStream(file);
MyObj obj = new MyObj(fis);
Why not have File as a constructor arg? Well, because it's much easier to write unit tests that supply a ByteArrayInputStream, than it is to create files as part of unit tests.
Scanner
is one class that will help you convert a stream of bytes into higher level types. DataInputStream
is another -- see which one suits your needs.
The approach you take will be guided by the format of the data. The easiest formats to use can be read by streaming - just chew through the stream handling each part as it comes. Some formats are awkward in that some crucial attribute you need before you start, is stored at the end of the file. In that case, you have to either:
byte[]
FileChannel.map()
to arbitrarily read bytes in any position.Another approach is to use Java's Serializable interface, and define your own writeObject and readObject methods to use the binary format you prefer.
Upvotes: 2
Reputation: 2877
Your approach seems ok to me. You will have to adhere to the data structure of the file so magic numbers are unavoidable.
You could avoid using those numbers in the helper functions by moving them up one level.
I mean implementing get methods for primitives e.g. instead of getRecordSize
you could use:
private int getShort(byte[] input, int index)
{
byte[] shortBytes= Arrays.copyOfRange(input,index,2);
return ByteBuffer.wrap(shortBytes).getShort();
}
So whenever you have to read a short you can use this function and just have to toss in the index.
Upvotes: 1