【问题标题】:why does BufferedReader fail to read the final line for an InputStream?为什么 BufferedReader 无法读取 InputStream 的最后一行?
【发布时间】:2013-09-02 07:35:15
【问题描述】:

*注意:为了澄清,也许这个问题对于 InputStream 是“活着的”不是很清楚。与 telnet 天气服务的连接(请参阅下面的链接)保持打开状态。目标是获取来自服务器的所有行。*

从示例 Apache WeatherTelnet code 构建,我使用 InputStream.read 来输出服务器结果(受 javamex tutorial 习惯用法的启发)一次一个字符,使用 chars 方法:

thufir@dur:~$ 
thufir@dur:~$ java -jar NetBeansProjects/Teln/dist/Teln.jar 
------------------------------------------------------------------------------
*               Welcome to THE WEATHER UNDERGROUND telnet service!            *
------------------------------------------------------------------------------
*                                                                            *
*   National Weather Service information provided by Alden Electronics, Inc. *
*    and updated each minute as reports come in over our data feed.          *
*                                                                            *
*   **Note: If you cannot get past this opening screen, you must use a       *
*   different version of the "telnet" program--some of the ones for IBM      *
*   compatible PC's have a bug that prevents proper connection.              *
*                                                                            *
*           comments: jmasters@wunderground.com                              *
------------------------------------------------------------------------------

Press Return to continue:
^Cthufir@dur:~$ 

这是所需的输出。但是,使用BufferedReader 读取InputStream 会导致删除最后一行。 (或者,至少,它没有打印到控制台。)错误的输出,使用lines 方法:

thufir@dur:~$ 
thufir@dur:~$ java -jar NetBeansProjects/Teln/dist/Teln.jar 

------------------------------------------------------------------------------

*               Welcome to THE WEATHER UNDERGROUND telnet service!            *

------------------------------------------------------------------------------

*                                                                            *

*   National Weather Service information provided by Alden Electronics, Inc. *

*    and updated each minute as reports come in over our data feed.          *

*                                                                            *

*   **Note: If you cannot get past this opening screen, you must use a       *

*   different version of the "telnet" program--some of the ones for IBM      *

*   compatible PC's have a bug that prevents proper connection.              *

*                                                                            *

*           comments: jmasters@wunderground.com                              *

------------------------------------------------------------------------------



^Cthufir@dur:~$ 
thufir@dur:~$ 

StreamReadercode:

package teln;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamReader {

    private StreamReader() {
    }

    StreamReader(InputStream inputStream) throws IOException {
        lines(inputStream);
    }

    private void chars(InputStream inputStream) throws IOException {
        do {
            char ch = (char) inputStream.read();
            System.out.print(ch);
        } while (true);
    }

    private void lines(InputStream inputStream) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        String line = "";
        do {
            System.out.println(line);
        } while ((line = br.readLine()) != null);

        System.out.println(line);

    }
}

大概最后一行是“null”吗?是否可以更改逻辑以便从lines 中打印最后一行,就像从chars 中一样?

【问题讨论】:

  • 嗯,你指的是哪一行?除了第二个版本中的空白行之外,我没有看到您的输出有任何差异……除非您的意思是“按回车键继续”,它看起来很可能根本不在输入数据中。 (您的chars 方法将继续执行,直到抛出异常,例如...)
  • 逐行读取有这个缺点,该行必须以\n字符结尾,如果没有,则不认为是完整的行。
  • @JohnKugelman:它仍然会被退回。我怀疑这里还有其他事情 - 不幸的是,我们没有足够的关于输入数据的信息来知道什么。一个简短但完整的程序来演示这个问题真的很有帮助。
  • 不是它不会... telnet 是一个懒惰的协议,它不会在最后一行结束,所以它不被认为是完整的...这就是为什么大多数 telnet 实现使用waitfor 例程
  • 最后看到的null是最后一个System.out.println(line);的原因

标签: java newline inputstream bufferedreader eol


【解决方案1】:

正如 Jean 所提到的,如果您知道输入永远不会以换行符 '\n' 或回车符 '\r' 字符结尾,那么这是使用 BufferReader 的一个缺点。在您的while 循环之后,您可能希望使用ready() 方法来测试在读取最后一行之后是否确实有更多文本,并使用read() 方法之一来提取剩余的文本。或者,将另一个文本阅读器全部替换为 BufferReader。

【讨论】:

【解决方案2】:

BufferedReader#readLine() 读取流直到到达\n、\r 或流的末尾,返回它读取到的内容或null。你没有向我们展示什么。为什么输出中的行之间有新行?

至于输出末尾的null

private void lines(InputStream inputStream) throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    String line = "";
    do {
        System.out.println(line); // will print an empty line to start
    } while ((line = br.readLine()) != null);

    System.out.println(line); // will print null

}

你的循环应该是

while ((line = br.readLine()) != null) {
    System.out.println(line);
}

避免打印第一个空的。另外,不要在while 循环之外打印line。很明显,它总是null(否则你不会在while之外)。

您的chars() 方法也在永远循环。

很可能您的服务器没有关闭流,因此BufferedReader 进行的底层read 调用没有返回-1 以指示EOF。您是在自己杀死应用程序吗?

【讨论】:

  • 我不确定为什么输出中的行之间有新行。起初我没有注意到,但尝试切换到 println,但这只是破坏了输出。是的,我自己杀死了该应用程序,因为它意味着无限期地保持打开状态。 (就像一个 MUD 客户端。)chars 方法应该永远循环,它来自链接教程。
  • 输出末尾没有null。如果他想在输出的开头有一个空行,那么while循环也没有错。
  • @Thufir 好了。因为readLine 调用尚未到达\n 或流的末尾,所以它阻塞了。
  • @JeanWaghetti 没有null,因为流被阻塞了,所以还没有到达执行和打印它的行。他方法中的最后一个println(line) 将打印null。你试过运行它吗?
  • @Thufir 一切都很好,但你明白发生了什么吗? readLine() 方法调用被阻止,已读取最后一行但尚未达到指示停止读取的内容。因此,包含最后一行的println() 永远不会被打印出来,所以你永远也看不到它。然后您自己关闭应用程序,因此您也不会在那里看到它。
【解决方案3】:

您还没有解释数据的来源,但我怀疑它来自某个在末尾没有行终止符的来源并且没有关闭流。

如果流已关闭 - 例如通过终止连接,如果这是一个客户端/服务器应用程序 - 那么您会看到最后一行。 BufferedReader 肯定会返回最后一行数据,即使它没有以行终止符结尾,但只有当它知道它到达流的末尾时。

【讨论】:

  • 数据来自svn.apache.org/repos/asf/commons/proper/net/tags/NET_1_0_0/src/…,该样本使用的天气服务器。我刚刚打破了一点,但同样的想法。使用TelnetClient。在不终止连接的情况下,我想得到最后一行。我对此有一些阅读要做!很多想法,谢谢大家:)
  • BufferedReader.readLine() 在发送行尾字符之前不会返回。这意味着如果您想在换行之前看到提示,您必须自己阅读字符。
【解决方案4】:

好的,这里是杂牌:

package teln;

import static java.lang.System.out;
import java.io.IOException;
import java.io.InputStream;

public class StreamReader {

    private StreamReader() {
    }

    StreamReader(InputStream inputStream) throws IOException {
        printEachLine(inputStream);
    }

    private void printEachLine(InputStream inputStream) throws IOException {
        int foo = 0;
        char ch = 0;
        StringBuilder sb = new StringBuilder();
        out.println("lines..");
        boolean isEOL = false;  //err, need to set this or get rid of it
        do {
            foo = inputStream.read();
            ch = (char) foo;
            //out.print(foo);
            sb.append(ch);
            if ((ch == 10)) {
                out.print(sb);
                sb = new StringBuilder();
                foo = inputStream.read();
                ch = (char) foo;
                sb.append(ch);
                if (ch != 13) {
                    while ((255 > ch) && (ch >= 0)) {
                        sb = new StringBuilder();
                        foo = inputStream.read();
                        ch = (char) foo;
                        sb.append(ch);
                        out.print(sb);
                    }
                }
                sb.append(ch);
            }

        } while (!isEOL);

    }
}

这取决于遵循特定模式的 LF 和 CR。 LF, 10, 后面总是跟 CR, 13,除非它是第一行或最后一行。因此,在这种特殊情况下,此代码将查找丢失的 CR 并将其识别为需要特殊输出。

但是,这是一个巨大的假设,并且可能特定于这种天气 telnet 服务。而且,看起来非常脆弱。

现在,我会继续这样做,但会继续寻找另一个答案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 2011-03-29
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多