【问题标题】:ObjectInputStream not reading full file after appending data附加数据后 ObjectInputStream 未读取完整文件
【发布时间】:2021-03-31 22:59:41
【问题描述】:

我正在编写一个小程序,在ArrayList 中插入客户详细信息并将其写入文件。

问题在于ObjectOutputStream 我能够通过转动FileOutputStream("",true) 将数据附加到文件中。但是当我尝试使用ObjectInputStream 读取数据时,它只会读取第一次插入的数据。但数据正在添加到文件中。

这是代码 -

public void insertCustomer() throws IOException 
{       
        Customer1=new customerDetails("1", "Moeen4", "654654", "asdf", "coding", "student", "65464", "3210");
        Customer3=new customerDetails("3", "Moeen5", "888888", "asdf", "coding", "student2", "65464", "321022");
        Customer4=new customerDetails("4", "Moeen6", "654654", "asdf", "coding", "student", "65464", "7890");
        
        _list=new ArrayList<customerDetails>();
        _list.add(Customer1);
        _list.add(Customer3);
        _list.add(Customer4);
        
        customersList cl=new customersList();
        
        cl.WriteObjectToFile(files._customers, _list);
        
        ArrayList<customerDetails>li=new ArrayList<customerDetails>();
        li= (ArrayList) cl.ReadObjectFromFile(files._customers);           
        for(int i=0;i<li.size();i++)
        {   System.out.println(li.size());
            System.out.println(li.get(i).Id);
            System.out.println(li.get(i).name);
            System.out.println(li.get(i).annual_Salary);
            System.out.println(li.get(i).Company);
            System.out.println(li.get(i).dateOfBirth);
            System.out.println(li.get(i).phone_Number);
        }
    }


public void WriteObjectToFile(String filepath,Object serObj) {       
    try {
        
        FileOutputStream fileOut = new FileOutputStream(filepath,true);
        ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
        objectOut.writeObject(serObj);
        objectOut.close();
        System.out.println("The Object  was succesfully written to a file");

    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

public Object ReadObjectFromFile(String filepath) {      
    try {

        FileInputStream fileIn = new FileInputStream(filepath);
        ObjectInputStream objectIn = new ObjectInputStream(fileIn);

        Object obj = objectIn.readObject();
        objectIn.close();
        System.out.println("The Object has been read from the file");
        
        return obj;

    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}

【问题讨论】:

  • 您只是从文件中读取一个 ArrayList。当您第二次写入时,另一个 ArrayList 将附加到文件的末尾。您需要继续阅读 ArrayLists 直到到达文件末尾。
  • 任何函数可以读取到文件末尾?
  • 你不能这样做。您不能在不采取特殊措施的情况下附加到ObjectOutputStream。最好每次都保持文件打开或完全重写。

标签: java arraylist file-io objectinputstream objectoutputstream


【解决方案1】:

这里真正的问题是:

  FileOutputStream fileOut = new FileOutputStream(filepath, true);
  ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
  objectOut.writeObject(serObj);

您不能像这样附加到现有的序列化。如果您在尝试读取附加到预先存在的(非空)文件的任何对象时遇到异常。

有一个技巧/hack 可以让你附加对象;见Appending to an ObjectOutputStream。 (技巧包括抑制对象流标头的写入。通过覆盖执行此操作的方法最容易完成。)

另一种方法是在writeObject 调用之间保持ObjectOutputStream 打开。但是,在某些用例中这是不可能的。

请注意,这两种方法之间存在语义差异。解释它的最好方法是,第一个行为就好像你每次写一个对象时都调用了reset();见javadoc


关于您的示例要注意的另一件事是您的阅读器代码仅读取一个对象。如果要读取多个对象,需要循环调用readObject。只有当你使用了上面的技巧 / hack 以避免编写虚假标题时,这才会起作用。

【讨论】:

    【解决方案2】:

    正如建议的那样,代码只读取第一个对象,您需要使用循环从多次写入中读取所有对象。

    但是 -

    如果您将上述代码(即ReadObjectFromFile)更改为使用循环,这将导致StreamCorruptedException: invalid type code: ACObjectOutputStream constructor 将序列化流标头写入OutputStream 即文件,当它使用new ObjectOutputStreamnew FileOutputStream(filepath, true) 关闭并重新打开时,将在附加点写入一个新标头,因此您将获得一个异常,因为标题只在文件开头出现一次

    这将需要处理,例如

    • 在持续时间内使用相同的ObjectOutputStream
    • 覆盖 java.io.ObjectOutputStream.writeStreamHeader() 以考虑附加到文件
    • 改变方法并使用List&lt;List&lt;Object&gt;&gt;,您可以将其作为一个整体进行读取、添加和写入。

    循环示例会抛出异常,除非 ObjectOutputStream 方法被改变

    public Object ReadObjectFromFile(String filepath) {
        
        List<List<Object>> objects = new ArrayList<>();
        
        FileInputStream fileIn = new FileInputStream(filepath);
        ObjectInputStream objectIn = new ObjectInputStream(fileIn);
        
        try {
            while (true) {
                List<Object> obj = (List<Object>) objectIn.readObject();
                // This will throw StreamCorruptedException: invalid type code: AC
                objects.add(obj);
                System.out.println("The Object has been read from the file");                
            }
        } catch (EOFException ex) {
            // ENDS WHEN ALL READ
        } finally {
            fileIn.close();
            objectIn.close();
        }
        
        return objects;
    }
    

    须藤代码List&lt;List&lt;Object&gt;&gt;方法-

    public void readAndWrite() {
        List<Object> customer = List.of(new CustomerDetails(...),
                                        new CustomerDetails(...),
                                        new CustomerDetails(...));
        
        List<List<Object>> objects = readFromFile("existing-customer-file.txt");
    
        objects.addAll(customer);
        
        writeObjectToFile(objects);
    }
    

    【讨论】:

    • `ObjectnputStream 根本不读取行,它读取对象,但是您当然可以读取任意数量的内容,正如您自己的代码所演示的那样。但是,在 OP 的上下文中,此代码将因另一个原因而失败。
    • @user207421 OP 没有提到异常被捕获,是的,它也会因 AC 错误而失败,你是对的,使用字线会更好,因为 header 我已经更新了这个,评论应该是建议List&lt;List&lt;Object&gt; 会更干净
    • 无论你是从哪里得到的,使用“对象”这个词都会比“行”或“标题”更好。 OP没有提到例外,因为他没有得到例外,但正如你所同意的那样,他会。而“使用catch 块来控制程序流”not“被大多数人认为是不好的做法”。这正是它的用途。并且OP在打开文件时并没有停止使用附加模式,您没有提到,他将继续从您发布的代码中获取invalid type code: AC
    • “OP 没有提到例外,因为他没有得到例外,但他会,正如你所同意的那样” - 这就是为什么它不在原始答案中的原因。通常读取一个文件知道它会抛出 Exception 被广泛认为是一种反模式,而不是说像 while(!(obj = inputStream.readObject()).equals(null)) 这样的东西并避免它。
    猜你喜欢
    • 1970-01-01
    • 2021-11-30
    • 2023-03-12
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 2020-10-25
    • 2022-06-21
    • 2018-11-19
    相关资源
    最近更新 更多