【问题标题】:Line reader fails depending on buffer size行阅读器失败取决于缓冲区大小
【发布时间】:2020-06-30 09:24:08
【问题描述】:

我需要逐行读取输入流。一条线被认为仅由 CRLF 终止,而不是由单个 CR 或 LF 终止。这排除了BufferedReaderreadLine() 并让我实现了自己的解决方案:

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[] bufferBufferedReader 中已经有一个缓冲区,不要无缘无故地复制功能)或你的队列。您只需要BufferedReaderStringBuilder 来构建行,StringBuilder 是您自己的readLine() 的局部变量。逻辑保持不变。
  • 感谢您的建议,但我真的很想了解为什么这段特定的代码不能按预期工作。
  • 通过调试器运行它?由于不需要的缓冲区导致一些逻辑错误。一个可行的解决方案可以用几行的静态方法来实现,所以调试你的实现对我来说不是很有吸引力。

标签: java junit reader


【解决方案1】:

事实证明,当 CR 是缓冲区中的最后一个字符时,我错误地附加了一个 CR,即使它后面跟着一个 LF。冗余 CR 还导致错误消息在我的控制台输出中被奇怪地截断。下面是工作方法:

String readLine() throws IOException
{
    while (lines.peek() == null)
    {
        if (line == null)
        {
            break;
        }

        final int read = reader.read(buffer);
        if (read == - 1)
        {
            lines.add(line.toString());
            line = null;
            continue;
        }

        int offset = 0;
        for (int i = 0; i < read; i++)
        {
            final char ch = buffer[i];

            if (cr)
            {
                if (ch == '\n')
                {
                    if (i != 0)
                    {
                        line.append(buffer, offset, i - 1 - offset);
                    }

                    offset = i + 1;

                    lines.add(line.toString());
                    line = new StringBuilder();

                    cr = false;
                }
                else
                {
                    if (i == 0)
                    {
                        line.append('\r');
                    }

                    if (ch != '\r')
                    {
                        cr = false;
                    }
                }
            }
            else if (ch == '\r')
            {
                cr = true;
            }
        }

        line.append(buffer, offset, read - offset - (cr ? 1 : 0));
    }

    return lines.poll();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-17
    • 1970-01-01
    • 1970-01-01
    • 2012-08-24
    • 2019-07-08
    • 1970-01-01
    相关资源
    最近更新 更多