问题描述
我正在尝试下载网页及其所有资源。首先,我下载 html,但何时确保保持文件格式并在下面使用此功能。 有问题,我在最终文件中找到了 10,当我发现 LF 或行转义的十六进制代码时。这给我的 javascript 函数带来了麻烦。
最终结果示例:
<!DOCTYPE html>10<html lang="fr">10 <head>10 <Meta http-equiv="content-type" content="text/html; charset=UTF-8" />10
有人可以帮我找到真正的问题吗?
public static String scanfile(File file) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
while (true) {
String readLine = bufferedReader.readLine();
if (readLine != null) {
sb.append(readLine);
sb.append(System.lineseparator());
Log.i(TAG,sb.toString());
} else {
bufferedReader.close();
return sb.toString();
}
}
} catch (IOException e) {
e.printstacktrace();
return null;
}
}
解决方法
您的代码存在多个问题。
字符集错误
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
这不会以棘手的方式起作用。
文件(以及,就此而言,网络服务器提供给您的数据)以字节为单位。一串数字,每个数字都在 0 到 255 之间。
那么,如果您是网络服务器并且想要发送字符 ö
,您发送的字节是什么?
答案很复杂。解释某些字符如何以字节形式呈现的映射称为字符集编码(缩写为“字符集”)。
任何时候字节变成字符,反之亦然,总是涉及到一个字符集。总是。
因此,您正在读取一个文件(即字节),并将其转换为 Reader(即字符)。因此,涉及字符集。
哪个字符集? new FileReader(path)
的 API 解释了哪一个:“系统默认”。 你不想那样。
因此,此代码已损坏。你想要两件事之一:
选项 1 - 按原样写入数据
在执行向网络服务器查询数据并将此信息中继到磁盘上的工作时,您只想存储字节(毕竟,网络服务器提供字节,磁盘存储字节,这很容易),但网络服务器也在标题中发送编码,您需要单独保存它。因为要读取那个'sack of bytes',你需要知道将它转换成字符的字符集。
你会怎么做?好吧,由你决定。例如,您可以规定数据文件以字符集编码的名称(通过该标头发送)开头,然后是 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 个错误,导致 1000 个错误和 1000 个堆栈跟踪出错,除了第一个之外,所有这些都是不受欢迎且无关紧要的,因此为什么这是可怕的代码,您永远不应该这样编写。
简单的解决方案是将 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。