【发布时间】:2020-06-30 09:24:08
【问题描述】:
我需要逐行读取输入流。一条线被认为仅由 CRLF 终止,而不是由单个 CR 或 LF 终止。这排除了BufferedReader 的readLine() 并让我实现了自己的解决方案:
final class LineReader
{
private final Reader reader;
private final char[] buffer;
private final Queue<String> lines = new LinkedList<>();
private StringBuilder line = new StringBuilder();
private boolean cr = false;
LineReader(final Reader reader, final int bufferSize)
{
this.reader = reader;
buffer = new char[bufferSize];
}
String readLine() throws IOException
{
while (lines.peek() == null)
{
final int read = reader.read(buffer);
if (read == - 1)
{
if (line == null)
{
return null;
}
// Reached EOF. Return the last line.
lines.add(line.toString());
line = null;
continue;
}
// Split the buffer by line.
int offset = 0;
for (int i = 0; i < read; i++)
{
final char ch = buffer[i];
if (cr)
{
// Last character was CR.
switch (ch)
{
case '\n':
// Found a CRLF.
if (i != 0)
{
line.append(buffer, offset, i - 1 - offset);
}
// Next line starts at the next character.
offset = i + 1;
lines.add(line.toString());
line = new StringBuilder();
cr = false;
break;
case '\r':
break;
default:
cr = false;
break;
}
}
else if (ch == '\r')
{
cr = true;
}
}
// Append remaining characters to the next line.
line.append(buffer, offset, read - offset);
}
return lines.poll();
}
}
最初,读者通过了一些简单的测试。但是,一旦我开始更改缓冲区大小,我注意到一些测试失败了。
@Test
void readLine() throws IOException
{
final String[] lines = new String[]{"foo bar", "baz", ""};
final String str = Stream.of(lines).collect(joining("\r\n"));
final Collection<Executable> assertions = new LinkedList<>();
for (int bufferSize = 1; bufferSize <= 10; bufferSize++)
{
final LineReader reader = new LineReader(new StringReader(str),
bufferSize);
assertions.add(() ->
{
for (int i = 0; i < lines.length; i++)
{
assertEquals(lines[i], reader.readLine());
}
assertNull(reader.readLine());
});
}
assertAll(assertions);
}
更具体地说,只有当缓冲区大小设置为 1、2、4 或 8 时,相等性断言才会失败。更奇怪的是,错误消息都是空白的。
Multiple Failures (4 failures)
>
>
>
>
org.opentest4j.MultipleFailuresError: Multiple Failures (4 failures)
>
>
>
>
at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:80)
at org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:54)
...
我无法解决这个问题。
【问题讨论】:
-
你自己的解决方案太复杂了。你不需要
char[] buffer(BufferedReader中已经有一个缓冲区,不要无缘无故地复制功能)或你的队列。您只需要BufferedReader和StringBuilder来构建行,StringBuilder是您自己的readLine()的局部变量。逻辑保持不变。 -
感谢您的建议,但我真的很想了解为什么这段特定的代码不能按预期工作。
-
通过调试器运行它?由于不需要的缓冲区导致一些逻辑错误。一个可行的解决方案可以用几行的静态方法来实现,所以调试你的实现对我来说不是很有吸引力。