Мне нужно читать входной поток построчно. Строка считается завершенной только 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)
...
Я не могу обернуться вокруг этого.