【问题标题】:Using 2 BufferedReaders to read 1 file使用 2 个 BufferedReaders 读取 1 个文件
【发布时间】:2021-04-02 13:13:02
【问题描述】:

所以我正在尝试从 .txt 文件创建一个二维字符数组。第一个 while 循环计算列数和行数。第二个while循环是将字符输入到二维数组中。但是,当我创建 BufferedReader br2 并使用 readLine() 然后尝试打印它时,该行打印出“null”。为什么第二个 BufferedReader 从文件末尾开始?

       public Maze(FileReader reader){
       try {
           BufferedReader br = new BufferedReader(reader);
           cols = 0;
           rows = 0;
           str = br.readLine();
           while (str != null) {
               if (str.length() > cols) {
                   cols = str.length();
               }
               rows++;
               str = br.readLine();
           }
       }
       catch (IOException e) {
           System.out.println("Error");
       }
        maze = new char[getNumRows()][getNumColumns()];
       try {
           BufferedReader br2 = new BufferedReader(reader);
           line = br2.readLine();
           System.out.println(line);
           while ((line = br2.readLine()) != null) {
               System.out.println(line);
               for (int i = 0; i < getNumColumns(); i++) {
                    maze[row][i] = line.charAt(i);
               }
               row++;
           }
       }
        catch (IOException e) {
            System.out.println("Error");
        }
    }

这就是我从 main 中调用它的方式

public class RobotTest {
    public static void main(String[] args) throws IOException{
        File file = new File(args[0]);
        Maze maze = new Maze(new FileReader(file));


    }
}

【问题讨论】:

  • 嗨@OscarTideman 这是因为您已经耗尽了底层读者。您可以在使用第一个 BufferedReader 之前先 mark(0),然后在第二个之前使用 reset()
  • 嘿@MrR 我尝试了mark(0),然后重新设置,但没有成功。一个快速的谷歌,我找到了一个他们使用标记(26)的例子,现在它以某种方式工作!

标签: java


【解决方案1】:

您正在使用同一个阅读器来初始化两个 BufferedReaders,并且在第一个阅读器完成阅读后,这意味着下一个阅读器将在 EOF 处继续阅读。在再次遍历它之前,您必须将第二个返回到文件的开头。

您可以使用FileReader.reset()重置阅读器的指针

您也可以在文档中查看mark()

来源:https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Reader.html#reset()

【讨论】:

  • FileReader 是否支持标记/重置?我真的没有在 javadocs 中看到它的证据。 BufferedReader 支持它。
  • FileReader 扩展了 InputStreamReader,它扩展了 Reader,所以是的,它支持它。
  • 并非所有阅读器都支持标记/重置,这就是阅读器有一个方法 markSupported() 的原因。我建议您阅读链接到自己的 javadocs。
  • 你错了。并非所有阅读器都支持 mark(),但所有扩展阅读器的阅读器都支持 reset()。该人在他的示例中清楚地使用了 FileReader。在写答案之前,我继续测试了这两种方法。
【解决方案2】:

顾名思义,“BufferedReader”使用缓冲区。

这是有原因的。

硬盘、网络通信、SSD - 这些都是倾向于以数据包的形式运行的概念。他们写或读大块。例如,通过网络,您不能只是“通过线路发送一堆字节”——您需要发送一个数据包,因为该数据包包含有关数据包应该去哪里以及解决排序问题的信息(当您在互联网上发送数据包,您稍后发送的数据包可能会更早到达,因此数据包上需要一个索引号,以便接收者可以以正确的方式重新排序)。

如果你发送一个字节,好吧 - 但那将是大约 200 个字节在线。因此,发送 1 个字节 5000 次大约是发送了 100 万个字节,而一次发送 5000 个字节只有 5200 个字节; 1000 倍的差异!

类似的原则也适用于其他地方,因此,“发送 1 个字节”或“读取 1 个字节”通常效率高达 1000 倍

因此,缓冲区。您从BufferedReader询问一个字符或一行(可能是很短的一行),它会尽职尽责地给您这个,但在引擎盖下它已经读取了整个较大的块(因为这是有效的),并将满足您的进一步要求,例如这个缓冲区的另一行,直到它用完,然后它会抓取另一个块。

所有这一切的结果是,一旦您将阅读器包装在缓冲阅读器中,您就永远无法再次使用阅读器。您现在已“承诺”到缓冲区:从现在开始,直到流完成为止,您可以读取 BufferedReader 的唯一内容。

您正在创建另一个,因此,您的代码有问题:您现在有效地跳过了第一个 BufferedReader 缓冲的内容;鉴于您得到了null,这意味着它现在缓冲了文件的全部内容,但也许在另一个系统上,一个更大的文件,它不会返回null,而是文件深处的一些行。无论哪种方式,一旦您创建了缓冲读取器,您就不能再使用该文件读取器。

解决方案很简单:制作一次缓冲读取器,然后传递它。不要一直用它制作 BufferedReader 实例。

此外,资源需要受到“保护”——无论代码如何退出,都必须关闭它们。如果您的代码抛出错误,您仍然需要关闭资源;不这样做意味着您的程序最终会卡住并且永远无法打开文件 - 唯一的出路是完全关闭应用程序。最后FileReader基本坏掉了;它使用“平台默认字符集编码”,这是任何人的猜测。您想“硬编码”它具有的编码,通常正确的答案是“UTF-8”。如果唯一的字符是简单的 ASCII,这并不重要,但现在是 2021 年。人们使用表情符号、雪人,而且地球上几乎所有语言都需要的不仅仅是 a 到 z。如果您的编码设置关闭,它将被破坏 gobbledygook。

较新的 Files API(java.io.File 已过时,您可能不想再使用它)默认为 UTF-8,这很好,节省了我们一些打字时间。

因此:

public static void main(String[] args) throws IOException {
    try (var reader = Files.newBufferedReader(Paths.get(args[0]))) {
        Maze maze = new Maze(reader);
    }
}

【讨论】:

    猜你喜欢
    • 2014-04-03
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 2018-09-06
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多