JAVA:如何在二进制文件中正确存储对象?不覆盖

时间:2017-04-29 17:39:00

标签: java file oop object binary

我正在研究Java文件I / O接口,我需要我的文件是二进制格式,而不是字符串。我发现ObjectOutputStream和ObjectInputStream对我的需求很有用,但我需要接口能够在我的文件末尾写入,因为我需要它来反复记录同一文件中的未来数据。

我尝试使用FileOutputStream(String file,boolean append)构造函数,但由于我的类实现了Serializable,因此似乎存在问题。当它尝试读取第二条记录时,会抛出 StreamCorruptedException 。我知道这一定是因为这个标题那种描述了对象的字段。

我的问题是,如何成功存储对象?我喜欢使用对象,因为它可以更容易地构造和处理数据。到目前为止,这是我的代码:

try
    {
        FileOutputStream outFile;
        ObjectOutputStream outStream;
        FileInputStream inFile;
        ObjectInputStream inStream;

        outFile = new FileOutputStream("people.mcf", true);
        outStream = new ObjectOutputStream(outFile);

        //Writing the objects
        System.out.println("Writing file...");
        for(int i = 0; i < 3; i++)
        {
            outStream.writeObject(people[i]);
        }
        System.out.println("Finished writing file...");
        outFile.close();
        outStream.close();
        //Reading the files
        System.out.println("Attempting to read file...");
        inFile = new FileInputStream("people.mcf");
        inStream = new ObjectInputStream(inFile);
        Person buffer;
        while(true)
        {                
            buffer = (Person)inStream.readObject();
            System.out.println(buffer.getData());
        }

    }
    catch(EOFException e)
    {
        System.out.println("Reached end of file...");
    }
    catch(IOException e)
    {
        System.err.println(e.toString());
    }

这是Person类:

static class Person implements Serializable
{
    private final String name;
    private final int age;
    private final String address;

    public Person(String name, int age, String address)
    {
        this.name = name;
        this.age = age;
        this.address = address;
    }               

    public String getData()
    {
        return name + " " + age + " " + address;
    }       
}

这是我得到的输出:

Writing file...
Finished writing file...
Attempting to read file...
Andres 26 Palo Gordo
Pilar 22 Palo Gordo
Kelvin 27 Palo Gordo
java.io.StreamCorruptedException: invalid type code: AC
BUILD SUCCESSFUL (total time: 0 seconds)
编辑:我不知道为什么我收到StreamCorruptedException,我知道ObjectOutputStream导致了这一点,我要求的是另一种存储对象数据的方法结构化的方式。

1 个答案:

答案 0 :(得分:0)

如果您的记录是固定长度或至少是MAXIMUM长度,则以下代码可以完成此任务。

使用RandomAccessFile,您将能够搜索文件指针以写入新内容,或更新已存在的记录。请参阅Indexed File in Wikipedia

为了更加轻松,您可以使用已ByteBuffer已经使用的FileChannel序列化记录,RandomAccessFile.getChannel()是RandomAccessFile的接口,请参阅{{3}}。

代码示例:

重新审视人员课程:

import java.nio.ByteBuffer;
import java.util.UUID;

public class Person implements ISerializable<UUID> {

   private final UUID   _id;
   private final String _name;
   private final int    _age;
   private final String _address;

   public Person( ByteBuffer source ) {
      _id      = SerializeUtil.unserializeUUID( source );
      _name    = SerializeUtil.unserializeString( source );
      _age     = source.get();
      _address = SerializeUtil.unserializeString( source );
   }

   public Person( String name, int age, String address) {
      _id      = UUID.randomUUID();
      _name    = name;
      _age     = age;
      _address = address;
   }

   @Override
   public UUID getKey() {
      return _id;
   }

   @Override
   public void serialize( ByteBuffer target ) {
      target.clear();
      SerializeUtil.serializeUUID  ( _id     , target );
      SerializeUtil.serializeString( _name   , target );
      target.put((byte)_age );
      SerializeUtil.serializeString( _address, target );
   }
}

IndexFile类:

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Function;

public class IndexedFile<K extends Comparable<K>, R extends ISerializable<K>>
   implements
      Closeable
{
   private final Map<K, Long>            _index = new TreeMap<>();
   private final RandomAccessFile        _file;
   private final FileChannel             _channel;
   private final ByteBuffer              _record;
   private final Function<ByteBuffer, R> _factory;

   public IndexedFile( File tracksFile, int recordSize, Function<ByteBuffer, R> factory ) throws IOException {
      _file    = new RandomAccessFile( tracksFile, "rw" );
      _channel = _file.getChannel();
      _record  = ByteBuffer.allocate( recordSize );
      _factory = factory;
      _channel.truncate( 0 );
   }

   @Override
   public void close() throws IOException {
      _file.close();
      _channel.close();
   }

   public int size() {
      return _index.size();
   }

   public int recordSize() {
      return _record.capacity();
   }

   public void put( R record ) throws IOException {
      final K    key    = record.getKey();
      final Long offset = _index.get( key );
      if( offset != null ) {
         _channel.position( offset );
      }
      else {
         final long pos = _channel.size();
         _channel.position( pos );
         _index.put( key, pos );
      }
      record.serialize( _record );
      _record.position( _record.limit());
      _record.flip();
      _channel.write( _record );
   }

   public R read( int recNo ) throws IOException {
      if( recNo >= _index.size()) {
         return null;
      }
      final long offset = recNo*_record.capacity();
      _channel.position( offset );
      _record.clear();
      _channel.read( _record );
      _record.flip();
      return _factory.apply( _record );
   }

   public R get( K key ) throws IOException {
      final Long offset = _index.get( key );
      if( offset == null ) {
         return null;
      }
      _channel.position( offset );
      _record.clear();
      _channel.read( _record );
      _record.flip();
      return _factory.apply( _record );
   }
}

为了完整性,实用程序类:

import java.nio.ByteBuffer;
import java.util.UUID;

public final class SerializeUtil {

   public static void serializeString( String s, ByteBuffer target ) {
      final byte[] bytes = s.getBytes();
      target.putInt( bytes.length );
      target.put( bytes );
   }

   public static String unserializeString( ByteBuffer target ) {
      final int length = target.getInt();
      final byte[] bytes = new byte[length];
      target.get( bytes );
      return new String( bytes );
   }

   public static void serializeUUID( UUID id, ByteBuffer target ) {
      target.putLong( id.getMostSignificantBits());
      target.putLong( id.getLeastSignificantBits());
   }

   public static UUID unserializeUUID( ByteBuffer source ) {
      final long msb = source.getLong();
      final long lsb = source.getLong();
      return new UUID( msb, lsb );
   }
}

接口ISerializable:

import java.nio.ByteBuffer;

public interface ISerializable<K extends Comparable<K>> {

   public K getKey();

   public void serialize( ByteBuffer target );
}