سؤال

أليس من الممكن إلحاق ObjectOutputStream?

أحاول إلحاق بقائمة من الكائنات. المقتطف التالي هو وظيفة يسمى كلما انتهت وظيفة.

FileOutputStream fos = new FileOutputStream
           (preferences.getAppDataLocation() + "history" , true);
ObjectOutputStream out = new ObjectOutputStream(fos);

out.writeObject( new Stuff(stuff) );
out.close();

ولكن عندما أحاول أن أقرأها، احصل على الأول فقط في الملف. ثم أحصل java.io.StreamCorruptedException.

لقراءة أنا أستخدم

FileInputStream fis = new FileInputStream
        ( preferences.getAppDataLocation() + "history");
ObjectInputStream in = new ObjectInputStream(fis);    

try{
    while(true)
        history.add((Stuff) in.readObject());
}catch( Exception e ) { 
    System.out.println( e.toString() );
}

لا أعرف عدد الكائنات التي ستكون موجودة لذلك أنا أقرأ بينما لا توجد استثناءات. مما تقول جوجل هذا غير ممكن. كنت أتساءل ما إذا كان أي شخص يعرف طريقة؟

هل كانت مفيدة؟

المحلول

ها هي الخدعة: الفئة الفرعية ObjectOutputStream وتجاوز writeStreamHeader طريقة:

public class AppendingObjectOutputStream extends ObjectOutputStream {

  public AppendingObjectOutputStream(OutputStream out) throws IOException {
    super(out);
  }

  @Override
  protected void writeStreamHeader() throws IOException {
    // do not write a header, but reset:
    // this line added after another question
    // showed a problem with the original
    reset();
  }

}

لاستخدامها، فقط تحقق مما إذا كان ملف السجل موجود أم لا ومثبات إما هذا الدفق الزوجي (في حالة وجود الملف = نحن إلحاقا = لا نريد أن نريد رأس) أو الدفق الأصلي (في حالة عدم وجود الملف = نحن بحاجة إلى رأس).

تعديل

لم أكن سعيدا بأول تسمية للفئة. هذا أفضل: فهو يصف "ما هو عليه" إلى حد ما "كيف يتم ذلك"

تعديل

غير الاسم مرة أخرى، لتوضيح، أن هذا الدفق هو فقط لإلحاق ملف موجود فقط. لا يمكن استخدامه لإنشاء الجديد ملف مع بيانات الكائنات.

تعديل

إضافة مكالمة إلى reset() بعد هذا السؤال أظهر أن النسخة الأصلية التي تجاوزت writeStreamHeader أن تكون غير المرجع يمكن أن تخلق بعض الشروط دفقا لا يمكن قراءته.

نصائح أخرى

كما api. يقول ObjectOutputStream يكتب المنشئ رأس تدفق التسلسل إلى الدفق الأساسي. ومن المتوقع أن يكون هذا الرأس مرة واحدة فقط، في بداية الملف. لذلك الاتصال

new ObjectOutputStream(fos);

عدة مرات على FileOutputStream أن يشير إلى نفس الملف سوف يكتب رأس عدة مرات وفساد الملف.

بسبب التنسيق الدقيق للملف المتسلسل، فإن إلحاقا سوف تفسد بالفعل. يجب عليك كتابة جميع الكائنات إلى الملف كجزء من نفس الدفق، وإلا فسوف يتعطل عندما يقرأ بيانات التعريف الموجي عندما يتوقع كائن.

يمكنك قراءة مواصفات التسلسل لمزيد من التفاصيل، أو (أسهل) هذا الموضوع حيث يقول رودي الأخضر أساسا ما قلته للتو.

أسهل طريقة لتجنب هذه المشكلة هي الاحتفاظ بفتح الإخراج عند كتابة البيانات، بدلا من إغلاقه بعد كل كائن. دعا reset() قد يكون من المستحسن تجنب تسرب الذاكرة.

سيكون البديل قراءة الملف كسلسلة من ObjectInputstreams المتتالية أيضا. ولكن هذا يتطلب منك الاحتفاظ بك عدد البايتات التي تقرأها (يمكن تنفيذ ذلك باستخدام filterInputtream)، ثم أغلق Inputstream، افتحه مرة أخرى، تخطي أن العديد من البايتات وخلفها فقط في ObjectInPuttream ().

ماذا عن كل مرة تقوم فيها بإلحاق كائن، وقراءة ونسخ جميع البيانات الحالية في الملف ثم الكتابة فوقها معا إلى ملف.

لقد مددت الحل المقبول لإنشاء فئة يمكن استخدامها لكلا ملف تعريف وإنشاء ملف جديد.

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

public class AppendableObjectOutputStream extends ObjectOutputStream {

    private boolean append;
    private boolean initialized;
    private DataOutputStream dout;

    protected AppendableObjectOutputStream(boolean append) throws IOException, SecurityException {
        super();
        this.append = append;
        this.initialized = true;
    }

    public AppendableObjectOutputStream(OutputStream out, boolean append) throws IOException {
        super(out);
        this.append = append;
        this.initialized = true;
        this.dout = new DataOutputStream(out);
        this.writeStreamHeader();
    }

    @Override
    protected void writeStreamHeader() throws IOException {
        if (!this.initialized || this.append) return;
        if (dout != null) {
            dout.writeShort(STREAM_MAGIC);
            dout.writeShort(STREAM_VERSION);
        }
    }

}

يمكن استخدام هذه الفئة كحل بديل مباشر للمظللات. يمكننا استخدام الفئة على النحو التالي:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ObjectWriter {

    public static void main(String[] args) {

        File file = new File("file.dat");
        boolean append = file.exists(); // if file exists then append, otherwise create new

        try (
            FileOutputStream fout = new FileOutputStream(file, append);
            AppendableObjectOutputStream oout = new AppendableObjectOutputStream(fout, append);
        ) {
            oout.writeObject(...); // replace "..." with serializable object to be written
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top