我可以理解为什么要这样做来读取旧文件格式。在这种情况下,默认Java序列化机制是一个障碍,而不是帮助。在某种程度上,你
可以
使用反射读取/写入类似结构的类。
样例代码:
public static class MyStruct {
public int foo;
public boolean bar = true;
public final byte[] byteArray = new byte[3];
}
public static void main(String[] args) throws IOException {
LegacyFileHandler handler = new LegacyFileHandler();
MyStruct struct = new MyStruct();
RandomAccessFile file = new RandomAccessFile("foo", "rw");
try {
for (int i = 0; i < 4; i++) {
struct.foo = i;
handler.write(file, struct);
}
struct = readRecord(file, handler, 2);
System.out.println(struct.foo);
} finally {
file.close();
}
}
private static MyStruct readRecord(RandomAccessFile file,
LegacyFileHandler handler, int n) throws IOException {
MyStruct struct = new MyStruct();
long pos = n * handler.sizeOf(struct);
file.seek(pos);
handler.read(file, struct);
return struct;
}
handler类;可以处理基元类型和字节数组,但不能处理其他内容:
public class LegacyFileHandler {
private final Map<Class<?>, Method> readMethods = createReadMethodMap();
private final Map<Class<?>, Method> writeMethods = createWriteMethodMap();
private Map<Class<?>, Method> createReadMethodMap() {
Class<DataInput> clazz = DataInput.class;
Class<?>[] noparams = {};
try {
Map<Class<?>, Method> map = new HashMap<Class<?>, Method>();
map.put(Boolean.TYPE, clazz.getMethod("readBoolean", noparams));
map.put(Byte.TYPE, clazz.getMethod("readByte", noparams));
map.put(Character.TYPE, clazz.getMethod("readChar", noparams));
map.put(Double.TYPE, clazz.getMethod("readDouble", noparams));
map.put(Float.TYPE, clazz.getMethod("readFloat", noparams));
map.put(Integer.TYPE, clazz.getMethod("readInt", noparams));
map.put(Long.TYPE, clazz.getMethod("readLong", noparams));
map.put(Short.TYPE, clazz.getMethod("readShort", noparams));
return map;
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
}
}
private Map<Class<?>, Method> createWriteMethodMap() {
Class<DataOutput> clazz = DataOutput.class;
try {
Map<Class<?>, Method> map = new HashMap<Class<?>, Method>();
map.put(Boolean.TYPE, clazz.getMethod("writeBoolean",
new Class[] { Boolean.TYPE }));
map.put(Byte.TYPE, clazz.getMethod("writeByte",
new Class[] { Integer.TYPE }));
map.put(Character.TYPE, clazz.getMethod("writeChar",
new Class[] { Integer.TYPE }));
map.put(Double.TYPE, clazz.getMethod("writeDouble",
new Class[] { Double.TYPE }));
map.put(Float.TYPE, clazz.getMethod("writeFloat",
new Class[] { Float.TYPE }));
map.put(Integer.TYPE, clazz.getMethod("writeInt",
new Class[] { Integer.TYPE }));
map.put(Long.TYPE, clazz.getMethod("writeLong",
new Class[] { Long.TYPE }));
map.put(Short.TYPE, clazz.getMethod("writeShort",
new Class[] { Integer.TYPE }));
return map;
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
}
}
public int sizeOf(Object struct) throws IOException {
class ByteCounter extends OutputStream {
int count = 0;
@Override
public void write(int b) throws IOException {
count++;
}
}
ByteCounter counter = new ByteCounter();
DataOutputStream dos = new DataOutputStream(counter);
write(dos, struct);
dos.close();
counter.close();
return counter.count;
}
public void write(DataOutput dataOutput, Object struct) throws IOException {
try {
Class<?> clazz = struct.getClass();
for (Field field : clazz.getFields()) {
Class<?> type = field.getType();
if (type == byte[].class) {
byte[] barray = (byte[]) field.get(struct);
dataOutput.write(barray);
continue;
}
Method method = writeMethods.get(type);
if (method != null) {
method.invoke(dataOutput, field.get(struct));
continue;
}
throw new IllegalArgumentException("Type "
+ struct.getClass().getName()
+ " contains unsupported field type " + type.getName()
+ " (" + field.getName() + ")");
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
public void read(DataInput dataInput, Object struct) throws IOException {
try {
Class<?> clazz = struct.getClass();
Object[] noargs = {};
for (Field field : clazz.getFields()) {
Class<?> type = field.getType();
if (type == byte[].class) {
byte[] barray = (byte[]) field.get(struct);
dataInput.readFully(barray);
continue;
}
Method method = readMethods.get(type);
if (method != null) {
Object value = method.invoke(dataInput, noargs);
field.set(struct, value);
continue;
}
throw new IllegalArgumentException("Type "
+ struct.getClass().getName()
+ " contains unsupported field type " + type.getName()
+ " (" + field.getName() + ")");
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}
此代码只进行了粗略的测试。
尝试将这样的读/写数据泛化存在问题。
-
处理任何对象类型都很困难,因为您必须自己将它们转换为字节数组,并确保它们保持固定的大小。
-
最大的问题可能是将所有字符串保持正确的长度——您需要了解一些有关Unicode的知识,以了解字符计数和编码字节大小之间的关系。
-
Java使用网络字节顺序,并没有真正的无符号类型。如果您正在读取遗留数据,可能需要执行一些位操作。
如果我需要处理二进制格式,我可能会将功能封装在专门的类中,这些类能够向I/O源写入/读取它们的状态。您仍然会在上面的列表中遇到问题,但这是一种更面向对象的方法。
如果我有选择文件格式的自由,我会选择
waqas suggested
并使用数据库。