【问题标题】:BufferedReader will not read final line of inputBufferedReader 不会读取输入的最后一行
【发布时间】:2015-12-24 03:23:54
【问题描述】:

我现在遇到了这个问题,正在解决一个问题(java 实践)。问题是要确保所提供输入的括号顺序正确(此链接中的更多信息:http://www.codeabbey.com/index/task_view/matching-brackets)。我面临的问题是我的 bufferedReader 不会读取我的最后一行输入。它进入最后一个循环,但似乎在阅读之前“暂停”。我可以让它工作的唯一方法是如果我按下回车键,那么程序最后一次继续通过 input.readLine() 并打印出我的字符串。这是我的代码:

public static void main(String[] args)
{

    try
    {
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("input data:");
    //First line is read to take in the number of lines for input will follow
    int data = Integer.parseInt(input.readLine());

    int i = 0;

    while(i < data)
    {
    //temp string builder to hold the wanted characters
    StringBuilder stringy = new StringBuilder();
    String line = input.readLine();
    //temp string builder holding the entire line
    StringBuilder sb = new StringBuilder(line);
        for(int j = 0; j < sb.length(); j++)
        {
            //loops through string builder & adds the wanted characters to stringy
            switch(sb.charAt(j)){
            case '(' : stringy.append(sb.charAt(j));
            break;

            case ')' : stringy.append(sb.charAt(j));
            break;

            case ']' : stringy.append(sb.charAt(j));
            break;

            case '[' : stringy.append(sb.charAt(j));
            break;

            case '{' : stringy.append(sb.charAt(j));
            break;

            case '}' : stringy.append(sb.charAt(j));
            break;
            }
        } 
        System.out.println(stringy);

        i++;
    }
    }catch(IOException x)
    {
        x.printStackTrace();
    }

}

对不起,如果我不够清楚。我试图在网上阅读这个,但人们似乎没有这个特定的问题。我不确定如何,但似乎在我的最后一行输入之前添加了额外的行或其他内容。感谢您的帮助,我真的很感激。

编辑: 抱歉,我意识到我没有为程序提供任何输入数据。这里是:

4
(a+[b*c]-{d/3}) 
(a + [b * c) - 17]
((a * x) + [b] * y) + c
auf(zlo)men [gy<psy>] four{s}

只需将上述内容复制并粘贴到您的程序中,您就会看到问题

【问题讨论】:

  • 程序似乎运行正常。例如,如果您在第一个 readLine()(数据)中输入 5,那么它将询问您 5 个新值来匹配括号。每次,您都必须按一次“输入”来提供这 5 个输入。那么,有什么问题呢?
  • 它工作正常检查这个ideone.com/uo8gN3
  • 对不起,我没有提供我一直使用的输入信息。我认为这可能与将输入复制并粘贴到服务器区域有关。
  • 我意识到你最终得到了所有的输入,我想我只是不明白为什么我需要在最后一行输入时按 Enter,即使它显示了我想要的前三行。 while 循环似乎在 bufferedReader 读取输入的最后一行之前或之前暂停。有点不同的问题,但这真的让我很烦恼。
  • 你的意思是你必须按Enter键加时?在我的上,您只需按一次即可进行最后一次输入。您是在 IDE 中运行,还是在命令行中运行?

标签: java stringbuilder


【解决方案1】:

总结

BufferedReader#readLine 方法阻止 I/O 读取输入,直到在输入中找到行终止符。输入的最后一行与所有其他行不同,因为它的末尾没有行终止符。在终端中按 enter 会添加所需的行终止符,但作为副作用,它还会导致终端将光标向下移动一行,从而导致您注意到的“行间空格”。这并不是真正的意外行为,也不是代码中的错误,但您可以通过确保在输入的最后一行末尾有一个行终止符来“修复”它。

细节

我可以重现您描述的行为。我编译代码,运行它,然后粘贴到示例输入中。就像你说的,它挂在最后一行。然后我按回车键,这会导致它继续,但为什么有必要这样做?在最后一个结果之前还有一个意想不到的差距。

> java Test
input data:
4
(a+[b*c]-{d/3}) 
(a + [b * c) - 17]
((a * x) + [b] * y) + c
auf(zlo)men [gy<psy>] four{s}([]{})
([)]
(()[])

()[]{}

我还注意到您没有提到的另一个问题。在上面示例的第七行(以“auf”开头的行),结果立即打印出来,而不移动到新行。

嘿,这是怎么回事?好吧,让我们尝试应用一些调试技术。 jstack 是 JDK 附带的一个工具,它允许您附加到正在运行的 JVM 并转储其执行线程的状态。这是一个很好的方式来了解你的代码在运行时真正在做什么。让我们尝试在进程似乎挂起时立即运行 jstack。首先,我需要确定 JVM 的进程 ID。让我们使用jps 来做到这一点。

> jps
83518 Test

> jstack 83518
2015-12-24 21:25:17
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.65-b04 mixed mode):

...

"main" prio=5 tid=0x00007fbba2001000 nid=0x1903 runnable [0x000000010a560000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:272)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
    - locked <0x00000007aaa9a5f0> (a java.io.BufferedInputStream)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
    - locked <0x00000007aab2ad88> (a java.io.InputStreamReader)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:154)
    at java.io.BufferedReader.readLine(BufferedReader.java:317)
    - locked <0x00000007aab2ad88> (a java.io.InputStreamReader)
    at java.io.BufferedReader.readLine(BufferedReader.java:382)
    at Test.main(Test.java:22)

...

我已修剪 jstack 的输出以仅显示相关的主线程。那很有意思。我可以看到主要入口点:Test.main。我可以看到对BufferedReader#readLine 的调用。在一系列其他方法调用之后,它到达FileInputStream#read。如果我多次运行 jstack,我会一直看到同样的事情。这意味着执行被困在试图从输入中读取字节的方法中。这很奇怪。那该怎么解释呢?可能BufferedReader#readLine 的 JavaDocs 包含对该行为的一些解释。

读取一行文本。行被视为由换行符 ('\n')、回车符 ('\r') 或回车符后紧跟换行符中的任何一种来终止。

此时,让我们尝试形成一个假设。什么可能导致进程在尝试读取数据时卡住? JavaDocs 说一行被认为是由特定字符终止的。也许我们的最后一行输入不包含行终止字符。

为了证实这一理论,让我们尝试查看我们输入的hexadecimal 转储。我通常使用xxd 命令行工具来执行此操作。这是结果。

0000000: 340a 2861 2b5b 622a 635d 2d7b 642f 337d  4.(a+[b*c]-{d/3}
0000010: 2920 0a28 6120 2b20 5b62 202a 2063 2920  ) .(a + [b * c) 
0000020: 2d20 3137 5d0a 2828 6120 2a20 7829 202b  - 17].((a * x) +
0000030: 205b 625d 202a 2079 2920 2b20 630a 6175   [b] * y) + c.au
0000040: 6628 7a6c 6f29 6d65 6e20 5b67 793c 7073  f(zlo)men [gy<ps
0000050: 793e 5d20 666f 7572 7b73 7d              y>] four{s}

我正在使用单个控制字符 LF(换行)来指示新行的 Mac 上进行测试。这在其他平台上可能会有所不同。最值得注意的是,Windows 使用 2 个控制字符的序列:CR/LF(回车/换行)。根据Unicode 标准,LF 的ASCII 代码是十六进制表示的0a。这显示在Basic Latin(ASCII) 代码图表中。回到我们的十六进制转储,我们可以看到 0a 字符出现了 4 次,并注意最后一行的末尾没有 0a 字符。

这开始看起来像是一个很有前途的理论。我们还能做些什么来验证它?感谢OpenJDK,我们可以查看到很多常用JDK类的源码实现,包括BufferedReader。让我们来看看BufferedReader#readLine 的实现。这是一个非常棘手的循环,但最重要的是它在名为eol 的变量中跟踪“行尾”,这就是导致它在fill 方法中停止填充其内部缓冲区并返回的条件给调用者的字符串。

charLoop:
    for (i = nextChar; i < nChars; i++) {
        c = cb[i];
        if ((c == '\n') || (c == '\r')) {
            eol = true;
            break charLoop;
        }
    }

    startChar = nextChar;
    nextChar = i;

    if (eol) {
        String str;
        if (s == null) {
            str = new String(cb, startChar, i - startChar);
        } else {
            s.append(cb, startChar, i - startChar);
            str = s.toString();
        }
        nextChar++;
        if (c == '\r') {
            skipLF = true;
        }
        return str;
    }

好的,现在我确信了!让我们通过重复我们的原始测试来测试假设,但这一次让我们确保我们在最后一行的末尾有一个行终止符。复制粘贴该版本的输入,我现在可以看到这些结果。

> java Test
input data:
4
(a+[b*c]-{d/3}) 
(a + [b * c) - 17]
((a * x) + [b] * y) + c
auf(zlo)men [gy<psy>] four{s}
([]{})
([)]
(()[])
()[]{}

这更像!

【讨论】:

  • 哇,非常感谢您的回答。这个解释正是我所需要的。我绝对计划将来使用这些工具来查找我的代码中的问题,我真的不知道它们存在!可能很容易看出我是一个新手程序员,但我将来会使用这些工具来调整我的技能。再次感谢您的回答,您确实为我清理了很多东西。
  • @DaveCooney,我很高兴听到它有帮助!不用担心。我们曾经都是新手。 :-) 如果这有帮助,请考虑接受答案和/或投票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 2011-03-29
  • 2013-09-02
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 2016-01-28
相关资源
最近更新 更多