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

Problem with input from user saved to file by RandomAccessFile methods

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

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

这些是我的属性 class 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() 将数据保存到文件时出了点问题),但即使我尝试了很少解决问题的方法,none 有效。

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

您使用不同的读写技术,它们不兼容。

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

Writes a string to the file using modified UTF-8 encoding in a machine-independent manner.

First, two bytes are written to the file, starting at the current file pointer, as if by the writeShort method giving the number of bytes to follow. This value is the number of bytes actually written out, not the length of the string. Following the length, each character of the string is output, in sequence, using the modified UTF-8 encoding for each character.

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');
    // ...