您的代码存在多个问题。
字符集错误
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
这不会以棘手的方式起作用。
文件(以及,就此而言,网络服务器提供给您的数据)以字节为单位。一个数字流,每个数字在 0 到 255 之间。
所以,如果你是一个网络服务器并且你想发送字符ö
,你发送什么字节?
答案很复杂。解释某些字符如何以字节形式呈现的映射称为字符集编码(缩写为“字符集”)。
任何时候将字节转换为字符,反之亦然,总会涉及到一个字符集。总是。
因此,您正在读取一个文件(即字节),并将其转换为 Reader(即字符)。因此,涉及字符集。
哪个字符集?APInew FileReader(path)
解释了哪一个:“系统默认值”。你不想那样。
因此,这段代码被破坏了。你想要两件事之一:
选项 1 - 按原样写入数据
在执行查询网络服务器的数据并将这些信息中继到磁盘的工作时,您只想存储字节(毕竟,网络服务器提供字节,而磁盘存储字节,这很容易),但网络服务器也会发送编码,在标题中,您需要单独保存。因为要读取“字节袋”,您需要知道字符集才能将其转换为字符。
你会怎么做?好吧,由你决定。例如,您可以规定数据文件以字符集编码的名称(通过该标头发送)开始,然后是0
字节,然后是未修改的数据。我认为你应该选择选项 2,但是
选项 2
对于基于文本的文档(HTML 是),另一个更好的选择是:读取数据时,将其转换为字符,使用标题告诉您的编码。然后,要将其保存到磁盘,使用 UTF-8 将字符转换回字节,这是一种很好的编码和行业标准。这样,在阅读时,您只知道它是 UTF-8,句号。
要读取 UTF-8 文本文件,请执行以下操作:
Files.newBufferedReader(Paths.get(file));
这样做的原因是,该Files
API 与大多数其他 API 不同(与 FileReader 不同,您永远不应该使用它),默认为 UTF_8 而不是平台默认值。如果你愿意,你可以让它更具可读性:
Files.newBufferedReader(Paths.get(file), StandardCharsets.UTF_8);
同样的事情 - 但现在在代码中很清楚发生了什么。
破碎的异常处理
} catch (IOException e) {
e.printStackTrace();
return null;
}
这是不行的——如果你捕捉到一个异常,要么 [A] 抛出其他东西,要么 [B] 处理问题。“记录并继续”绝对不是“处理”它。您的异常处理策略导致 1 个错误,导致一千个堆栈跟踪出现一千个错误,除了第一个之外,所有这些都是不希望的和不相关的,因此为什么这是可怕的代码,您永远不应该这样编写它。
简单的解决方案就是throws IOException
使用您的scanFile
方法。该方法固有地与文件交互,它应该抛出那个。请注意,您的psv main(String[] args)
方法可以并且通常应该声明为throws Exception
.
它还使您的代码更简单、更短,耶!
资源管理失败
文件阅读器是一种资源。无论发生什么,您都必须关闭它。你没有这样做:如果.readLine()
抛出异常,那么你的代码将跳转到 catch 处理程序并且bufferedReader.close
永远不会执行。
解决方案是使用 ARM(自动资源管理)构造:
try (var br = Files.newBufferedReader(Paths.get(file), StandardCharsets.UTF_8)) {
// code goes here
}
close()
无论“代码到这里”块如何退出,此构造都确保调用它。即使它通过异常或return
语句“退出”。
问题
您的“读取文件并打印”代码除了上述三项之外大部分都很好。问题是磁盘上的 HTML 文件已损坏;错误在于您的代码从 Web 服务器读取数据并将其保存到磁盘。您没有粘贴该代码。
具体来说,System.lineSeparator()
返回实际的字符串。因此,假设您粘贴的代码确实是您正在运行的代码,如果您看到实际的“10”出现,那么这意味着磁盘上的 HTML 文件在那里。这不是读取的代码。
结束的想法
更一般地说,“只需在磁盘上打印具有已知编码的文件”的工作可以用更少的代码行来完成:
public static String scanFile(String path) throws IOException {
return Files.readString(Paths.get(path));
}
您应该只使用上面的代码。它简单,简短,没有任何错误,不会泄漏资源,具有适当的异常处理,并且将使用 UTF-8。