BufferedReader不会读取最终输入行

时间:2015-12-24 03:23:55

标签: java stringbuilder

我现在遇到这个问题解决问题(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}

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

1 个答案:

答案 0 :(得分:2)

摘要

BufferedReader#readLine方法阻塞I / O读取输入,直到它在输入中找到行终止符。输入的最后一行与所有其他行不同,因为它的末尾没有行终止符。在终端中按Enter键会添加所需的行终止符字符,但作为副作用,它还会使终端将光标向下移动一行,从而导致&#34;之间的空格。你注意到的那些线条。这不是一个非常意外的行为,它不是代码中的错误,但您可以修复&#34;它通过确保在输入的最后一行末尾有一个行终止符。

详情

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

> 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}([]{})
([)]
(()[])

()[]{}

我还注意到你没有提到的另一个问题。在上面示例的第七行(以&#34; auf&#34;开头的行)中,结果随后打印而不移动到新行。

嘿,这里发生了什么?好吧,让我们尝试应用一些调试技巧。 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包含对行为的一些解释。

  

读取一行文字。一条线被认为是换行(&#39; \ n&#39;),回车(&#39; \ r&#39;)或回车后紧接着换行符中的任何一条终止

此时,让我们尝试形成一个假设。什么可能导致进程在尝试读取数据时遇到困难? 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(回车/换行)。根据{{​​3}}标准,LF的ASCII代码是十六进制表示的0a。这显示在Unicode代码表中。回到我们的十六进制转储,我们可以看到0个字符出现4次,并注意到最后一行末尾没有0a字符。

这开始看起来像一个有前途的理论。我们还可以做些什么来验证它?感谢Basic Latin(ASCII),我们可以查看许多常见JDK类的源代码实现,包括BufferedReader。让我们尝试一下OpenJDK的实现。这是一个非常棘手的循环,但最重要的是它跟踪&#34;行结束&#34;在名为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}
([]{})
([)]
(()[])
()[]{}

那更像是它!