使用 LF 的十六进制代码将缓冲区转换为字符串时的问题

问题描述

我正在尝试下载网页及其所有资源。首先,我下载 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。