没有内容写入 .dat 文件

Nothing being written to a .dat file

我正在尝试创建一个程序来读取包含 HashMap 对象的 .dat 文件。如果 .dat 文件是空的,它应该向文件写入一个空的 HashMap 对象,然后,它应该给用户选项来编辑 HashMap 对象。

当我第一次使用空 .dat 文件“playlist.dat”尝试此操作时,它试图将空 HashMap 对象写入文件,但随后触发了EOFException。我查看了文件,发现它是空的。

处理此问题的代码在这里:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Scanner;

@SuppressWarnings("unchecked") //was the only way to stop the error: "Type safety: Unchecked cast from Object to HashMap<String,Song>" from happening

public class Main {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        // Map of songs
        HashMap<String, Song> playList = new HashMap<String, Song>();

        // getting file name where it is stored
        Scanner input = new Scanner(System.in);
        System.out.print("Enter the file name you stored/will store your playlist in: ");
        String fileName = input.nextLine();
        input.close();

        File file = new File(fileName);
        
        ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(file))); //using output streams to find the file requested by user

        if (file.length() == 0) {
            out.writeObject(playList);
            //System.out.println("Wrote playlist"); //debug
        } //sees if there is nothing in the file, and if there is nothing, writes a blank HashMap to it

        ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file))); //This is where the EOFException is taking place. In the if statement above, it tests if the file is empty. Since playlist.dat is empty, it will attempt to write an empty HashMap playlist to the file, but since there is an EOFException and playlist.dat is empty, it is clearly not doing this.
        
        playList = (HashMap<String, Song>) in.readObject();
        System.out.println("Opening playlist at " + fileName + "."); //debug
        in.close();

/* other code continues this way... */

几件事:

  1. 对象流非常脆弱且不理想。 Java 的不同版本的用户可能无法在不同于其序列化版本的版本中打开您的 HashMap。相反,我推荐 XML(已包含在 JRE 中)或 JSON(我相信您需要 Jackson 库......除非它已被添加到 JRE 中)

  2. 您不需要缓冲输入或输出流。首先,缓冲流用于 低效 读取和写入(如逐行读取,或写入(不是一次全部)到文件。还有对象流和 PrintWriter / PrintStream(其中你没有使用,但为了完整性我已经包括在内)已经内置了缓冲能力。

  3. 您应该 绝对 在尝试从流中读取之前关闭您的写入流。我相信if/when你这样做,你会在阅读文件时看到文件中的内容

BufferedOutputStream 具有大小等于 8192 字节 的内部缓冲区。直到数据超出缓冲区或者您 flush 整个数据都保存在缓冲区中,因此不会写入文件

似乎播放列表数据小于缓冲区大小 - 您需要在写入完成后对 out 输出流调用 close() 方法或使用 try-with-resources 方法

基本上你想要 serialize 一个对象,特别是 HashMap 的一个实例,然后 de-serialize 它。

java 中的序列化仅存储 class 成员的值。它使用实际 .class 文件与 .dat 文件的组合来 de-serialize 一个对象。因此,如果您序列化一个空的 HashMap.dat 文件将 [几乎] 为空。在您的情况下,它是空的,因为您在写入文件后没有关闭文件。

下面的代码是序列化和 de-serializing 一个 HashMap 的最小示例。请注意,它使用 try-with-resources to ensure that the .dat files are closed after use. It also uses multi-catch 来处理异常。

代码的解释出现在它后面。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;

public class SongList {

    public static void main(String[] args) {
        File f = new File("songlist.dat");
        try (FileOutputStream fos = new FileOutputStream(f);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            HashMap<String, String> playList = new HashMap<>();
            oos.writeObject(playList);
        }
        catch (IOException xIo) {
            xIo.printStackTrace();
        }
        try (FileInputStream fis = new FileInputStream(f);
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            HashMap<?, ?> playList = (HashMap<?, ?>) ois.readObject();
        }
        catch (IOException | ClassNotFoundException x) {
            x.printStackTrace();
        }
    }
}
  • BufferedOutputStreamBufferedInputStream 不是必需的。
  • 在运行时,当您 de-serialize HashMap 时,java 无法知道 HashMap 中条目的类型。这就是你收到警告的原因。请注意,这是警告而不是错误。这就是为什么我在读取 .dat 文件时使用 ? 通配符的原因。
  • 由于您没有post class Song 的代码,我将 HashMap 值类型更改为 String 以保留内容简单的。您可以继续在代码中使用 Song
  • 创建一个新文件 FileOutputStream 创建一个新的空文件。为已存在的文件创建新的 FileOutputStream 将删除文件内容。如果要追加到现有文件,请使用 this constructor of class FileOutputStream.

顺便说一句,你不应该关闭包装标准输入的Scanner