BufferedReader 不会读取最后一行输入
BufferedReader will not read final line of input
我现在遇到这个问题正在解决一个问题(练习 java)。问题是要确保所提供输入的括号顺序正确(更多信息在 link: http://www.codeabbey.com/index/task_view/matching-brackets 中)。我面临的问题是我的 bufferedReader 不会读取我的最后一行输入。它进入了最后一个循环,但在阅读之前似乎 "pause"。我能让它工作的唯一方法是如果我按回车键,然后程序继续最后一次通过 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}
只需将上面的内容复制并粘贴到您的程序中,您就会看到问题
总结
BufferedReader#readLine
方法会阻止 I/O 读取输入,直到它在输入中找到行终止符。输入的最后一行与所有其他行不同,因为它末尾没有行终止符。在终端中按 enter 会添加所需的行终止符,但作为副作用,它也会导致终端将其光标向下移动一行,从而导致您注意到 "space in between" 行。这并不是真正的意外行为,也不是您代码中的错误,但您可以 "fix" 通过确保在输入的最后一行末尾有一个行终止符来实现它。
详情
我可以重现您描述的行为。我编译代码,运行 它,然后粘贴到示例输入中。就像你说的,它挂在最后一行。然后我按回车键,这使它继续进行,但为什么这是必要的?在最后的结果之前还有意想不到的差距。
> 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 is a tool that ships with the JDK that lets you attach to a running JVM and dump the state of its threads of execution. It's a great way to get a glimpse at what your code is really doing when it runs. Let's try running jstack right when the process appears to hang. First though, I need to identify the process ID of the JVM. Let's use 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 包含对行为的一些解释。
Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
至此,让我们尝试形成一个假设。什么可能导致进程在尝试读取数据时卡住? JavaDocs 说一行被认为是由特定字符终止的。也许我们的最后一行输入不包含行终止符。
为了证实这一理论,让我们尝试查看一个 hexadecimal dump of our input. I usually use the 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}
我正在测试 Mac,它使用单个控制字符 LF(换行符)来指示新行。这在其他平台上可能有所不同。最值得注意的是,Windows 使用 2 个控制字符的序列:CR/LF(回车 return/line 进给)。根据Unicode standard, the ASCII code for LF is 0a in hex representation. This is shown in the Basic Latin(ASCII)码表。回到我们的十六进制转储,我们可以看到 0a 字符出现了 4 次,请注意最后一行的末尾没有 0a 字符。
这开始看起来像是一个很有前途的理论。我们还能做些什么来验证它?感谢 OpenJDK, we can view the source code implementation of many of the common JDK classes, including BufferedReader
. Let's try looking at the implementation of BufferedReader#readLine
。这是一个非常棘手的循环,但底线是它在名为 eol
的变量中跟踪 "end of line",这就是导致它停止填充 fill
中的内部缓冲区的条件方法,而不是 return 一个字符串给调用者。
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;
}
OK,我信服了!让我们通过重复我们的原始测试来检验假设,但这次让我们确保我们在最后一行的末尾有一个行终止符。复制粘贴输入的那个版本,我现在看到了这些结果。
> 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}
([]{})
([)]
(()[])
()[]{}
更像了!
我现在遇到这个问题正在解决一个问题(练习 java)。问题是要确保所提供输入的括号顺序正确(更多信息在 link: http://www.codeabbey.com/index/task_view/matching-brackets 中)。我面临的问题是我的 bufferedReader 不会读取我的最后一行输入。它进入了最后一个循环,但在阅读之前似乎 "pause"。我能让它工作的唯一方法是如果我按回车键,然后程序继续最后一次通过 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}
只需将上面的内容复制并粘贴到您的程序中,您就会看到问题
总结
BufferedReader#readLine
方法会阻止 I/O 读取输入,直到它在输入中找到行终止符。输入的最后一行与所有其他行不同,因为它末尾没有行终止符。在终端中按 enter 会添加所需的行终止符,但作为副作用,它也会导致终端将其光标向下移动一行,从而导致您注意到 "space in between" 行。这并不是真正的意外行为,也不是您代码中的错误,但您可以 "fix" 通过确保在输入的最后一行末尾有一个行终止符来实现它。
详情
我可以重现您描述的行为。我编译代码,运行 它,然后粘贴到示例输入中。就像你说的,它挂在最后一行。然后我按回车键,这使它继续进行,但为什么这是必要的?在最后的结果之前还有意想不到的差距。
> 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 is a tool that ships with the JDK that lets you attach to a running JVM and dump the state of its threads of execution. It's a great way to get a glimpse at what your code is really doing when it runs. Let's try running jstack right when the process appears to hang. First though, I need to identify the process ID of the JVM. Let's use 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 包含对行为的一些解释。
Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.
至此,让我们尝试形成一个假设。什么可能导致进程在尝试读取数据时卡住? JavaDocs 说一行被认为是由特定字符终止的。也许我们的最后一行输入不包含行终止符。
为了证实这一理论,让我们尝试查看一个 hexadecimal dump of our input. I usually use the 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}
我正在测试 Mac,它使用单个控制字符 LF(换行符)来指示新行。这在其他平台上可能有所不同。最值得注意的是,Windows 使用 2 个控制字符的序列:CR/LF(回车 return/line 进给)。根据Unicode standard, the ASCII code for LF is 0a in hex representation. This is shown in the Basic Latin(ASCII)码表。回到我们的十六进制转储,我们可以看到 0a 字符出现了 4 次,请注意最后一行的末尾没有 0a 字符。
这开始看起来像是一个很有前途的理论。我们还能做些什么来验证它?感谢 OpenJDK, we can view the source code implementation of many of the common JDK classes, including BufferedReader
. Let's try looking at the implementation of BufferedReader#readLine
。这是一个非常棘手的循环,但底线是它在名为 eol
的变量中跟踪 "end of line",这就是导致它停止填充 fill
中的内部缓冲区的条件方法,而不是 return 一个字符串给调用者。
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;
}
OK,我信服了!让我们通过重复我们的原始测试来检验假设,但这次让我们确保我们在最后一行的末尾有一个行终止符。复制粘贴输入的那个版本,我现在看到了这些结果。
> 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}
([]{})
([)]
(()[])
()[]{}
更像了!