用户输入通过 RandomAccessFile 方法保存到文件的问题

问题描述

我遇到了来自用户的输入问题。我需要将用户的输入保存到二进制文件中,当我读取它并将其显示在屏幕上时,它无法正常工作。我不想放几百行,所以我会尽量用更紧凑的形式来描述它。项目属性中 NetBeans 中的编码为“UTF-8”

我在 NetBeans 控制台或 cmd 控制台中收到了来自用户的输入。然后我将它保存到由字符串组成的对象中,然后将它添加ArrayList<Ksiazka> 中,其中 Ksiazka 是我的类(基本上是一本书的属性)。然后我将整个 ArrayList 对象保存到文件 baza.bin。我通过循环遍历类 Ksiazka 的整个对象列表来做到这一点,将每个字符串一个一个地保存到文件 baza.bin 中,使用方法 writeUTF(oneOfStrings)。当我尝试阅读文件 baza.bin 时,我看到的是问号而不是特殊字符(ą、ć、ę、ł、ń、ó、ś、ź)。我认为文件和输入数据的编码差异存在问题,但老实说我不知道​​如何解决

这些是我的类 Ksiazka属性

private String id;
private String tytul;
private String autor;
private String rok;
private String wydawnictwo;
private String gatunek;
private String opis;
private String ktoWypozyczyl;
private String kiedyWypozyczona;
private String kiedyDoOddania;

这是从用户读取数据的方法

static String podajDana(String[] tab,int copokazac){
    System.out.print(tab[copokazac]);
    boolean podawajDalej = true;
    String linia = "";
    Scanner klawiatura = new Scanner(system.in,"utf-8"); 
    do{
        try {   
            podawajDalej = false; 
            linia = klawiatura.nextLine();
        }
        catch(NoSuchElementException e){
            System.err.println("Wystąpił błąd w czasie podawania wartości!"
                    + " Spróbuj jeszcze raz!");
        }
        catch(IllegalStateException e){
            System.err.println("Wewnętrzny błąd programu typu 2! Zgłoś to jak najszybciej"
                    + " razem z tą wiadomością");
        }
    }while(podawajDalej);
    return linia; 
}

String[] tab 只是我希望能够在屏幕上显示的字符串数组,每个集合(数组)都有自己的功能int copokazac 是我想要显示的数组中的行数.

这个将所有数据从 ArrayList<Ksiazka> 保存到文件 baza.bin:

static void zapiszZmiany(ArrayList<Ksiazka> bazaKsiazek){
     try{
        RandomAccessFile plik = new RandomAccessFile("baza.bin","rw");
        for(int i = 0; i < bazaKsiazek.size(); i++){
            plik.writeUTF(bazaKsiazek.get(i).zwrocId());
            plik.writeUTF(bazaKsiazek.get(i).zwrocTytul());
            plik.writeUTF(bazaKsiazek.get(i).zwrocAutor());
            plik.writeUTF(bazaKsiazek.get(i).zwrocRok());
            plik.writeUTF(bazaKsiazek.get(i).zwrocWydawnictwo());
            plik.writeUTF(bazaKsiazek.get(i).zwrocgatunek());
            plik.writeUTF(bazaKsiazek.get(i).zwrocopis());
            plik.writeUTF(bazaKsiazek.get(i).zwrocKtoWypozyczyl());
            plik.writeUTF(bazaKsiazek.get(i).zwrocKiedyWypozyczona());
            plik.writeUTF(bazaKsiazek.get(i).zwrocKiedyDoOddania());
        }

        plik.close();
            }
        catch (FileNotFoundException ex){
            System.err.println("Nie znaleziono pliku z bazą książek!");
        }
        catch (IOException ex){
            System.err.println("Błąd zapisu bądź odczytu pliku!");
        }
}

我认为这两种方法之一存在问题(要么我在阅读时做错了什么,要么在使用 writeUTF() 将数据保存到文件时出错),但即使如此,我也尝试了一些解决它,他们都没有工作。

与讲师快速交谈后,我得到了最多可以使用 JDK 8 的信息。

解决方法

您使用了不同的阅读和写作技巧,而且它们不兼容。

尽管名称如此,但 RandomAccessFile 的 writeUTF 方法不写入 UTF-8 字符串。来自the documentation

以与机器无关的方式使用 modified UTF-8 编码将字符串写入文件。

首先,从当前文件指针开始,将两个字节写入文件,就像通过 writeShort 方法给出要跟随的字节数一样。该值是实际写出的字节数,而不是字符串的长度。按照长度依次输出字符串的每个字符,对每个字符使用修改后的 UTF-8 编码。

writeUTF 会写一个两字节的长度,然后把字符串写成 UTF-8,除了 '\u0000' 字符写成两个 UTF-8 字节,补充字符写成两个 UTF-8 编码的代理,而不是单个 UTF-8 代码点序列。

另一方面,您正在尝试使用 new Scanner(System.in,"utf-8")klawiatura.nextLine(); 读取该数据。这种方法不兼容,因为:

  • 文本不是真正的 UTF-8 序列。
  • 在写入文本之前,写入了表示其数字长度的两个字节。它们不是可读的文本。
  • writeUTF 不写换行符。事实上,它根本不写任何终止序列。

最好的解决方案是删除所有 RandomAccessFile 的用法并用 Writer 替换它:

Writer plik = new FileWriter(new File("baza.bin"),StandardCharsets.UTF_8);
for (int i = 0; i < bazaKsiazek.size(); i++) {
    plik.write(bazaKsiazek.get(i).zwrocId());
    plik.write('\n');
    plik.write(bazaKsiazek.get(i).zwrocTytul());
    plik.write('\n');
    // ...