Java 中 BufferedReader.readLine() 的最大行长?

Posted

技术标签:

【中文标题】Java 中 BufferedReader.readLine() 的最大行长?【英文标题】:Maximum line length for BufferedReader.readLine() in Java? 【发布时间】:2011-08-23 01:33:53 【问题描述】:

我使用 BufferedReader 的 readLine() 方法从套接字读取文本行。

没有明显的方法来限制读取的行长度。

我担心数据源会(恶意或错误地)写入大量数据而没有任何换行符,这将导致 BufferedReader 分配无限量的内存。

有没有办法避免这种情况?还是我必须自己实现readLine() 的有界版本?

【问题讨论】:

一次读取一条数据怎么样,比如 1KB 或 4KB? 如何使用来自您服务器的“OuptputStream”的“newLine()”方法? 便利方法经常出现这种情况:一旦您有更具体的要求,就不再是便利,而是变得烦人;-) 您必须“手动”实现。 @Srinivas Reddy Thatiparthy - 当然,但我必须自己寻找行分隔符 ***.com/a/17142341/748087 【参考方案1】:

最简单的方法是实现您自己的有界线阅读器。

或者更简单,重用来自this BoundedBufferedReader class的代码。

实际上,编写与标准方法相同的readLine() 并非易事。正确处理 3 种行终止符需要一些非常仔细的编码。将上述链接的不同方法与 BufferedReader 的 Sun version 和 Apache Harmony version 进行比较是很有趣的。

注意:我不完全相信有界版本或 Apache 版本是 100% 正确的。有界版本假设底层流支持标记和重置,这当然不总是正确的。如果 Apache 版本将 CR 视为缓冲区中的最后一个字符,则它似乎会预读一个字符。读取用户输入的输入时,这会在 MacOS 上中断。 Sun 版本通过设置一个标志来处理这个问题,以导致在下一个read... 操作时跳过 CR 之后可能的 LF;即没有虚假的预读。

【讨论】:

或者你可以窃取它:code.google.com/p/owasp-esapi-java/issues/… 在 InputStream 级别建立对读入数据量的限制并保留解码行的逻辑可能更简单。 @Neil - 是的。请参阅@Tom Hawtin 的回答。 @GáborLipták - 如果你想追踪它,我会修复它。 @GáborLipták - 我想就是这样。我更新了代码本身的链接,现在在 Github 上。谢谢。【参考方案2】:

另一个选项是 Apache Commons 的BoundedInputStream:

InputStream bounded = new BoundedInputStream(is, MAX_BYTE_COUNT);
BufferedReader reader = new BufferedReader(new InputStreamReader(bounded));
String line = reader.readLine();

【讨论】:

赞成让别人为你做艰苦的工作:D 这不是仅适用于 1 字节 == 1 个字符的情况吗?当您处理 UTF-16 时,这实际上是减半。 @Renan 是的,在上面的示例中,假设 MAX_LINE_SIZE 以字节为单位定义。在 OP 描述的场景中,输入数据是未知的并且可能是恶意的,因此您不能真正假设有关编码的任何事情。因此,基于字节数的限制似乎是最好的。但是,如果您的用例具有具有已知多字节编码的可信数据,那么您可以相应地进行调整。我将编辑变量名称以使其更明确;) 链接无效,否则我喜欢这个答案。这是更新的link 如何使用它。请提供详细的使用步骤。它在 Java 本身中并不容易获得【参考方案3】:

字符串的限制是 20 亿个字符。如果希望限制更小,则需要自己读取数据。您可以从缓冲流中一次读取一个字符,直到达到限制或换行字符。

【讨论】:

BufferedReader 并非微不足道。自己实现不是一个好选择。 @JeffreyBlattman 但这就是 seantmalone 在接受的答案中所做的。您认为什么以及更好的选择? 和santmalone 的解决方案只适用于支持标记/重置的读者。这个怎么样? github.com/pjpmarques/pmarques.util.io/blob/master/src/main/…【参考方案4】:

也许最简单的解决方案是采用稍微不同的方法。与其试图通过限制一次特定的读取来阻止 DoS,不如限制读取的原始数据的总量。通过这种方式,您无需担心每次读取和循环都使用特殊代码,只要分配的内存与传入数据成比例。

您可以测量Reader,或者可能更恰当地测量未解码的Stream 或等效值。

【讨论】:

你打算怎么做? meter 阅读器或流是什么意思? @Daphna Shezaf 实现FilterInputStream,覆盖reads,返回计数字节。类似的东西。 我认为你的建议只有在从套接字接收的数据总量可以限制的情况下才有帮助。就我而言,我可以接收无限数量的消息,我只想限制消息长度。 @Daphna Shezaf 没有什么可以阻止您在每条消息之后、阅读该行之后或中间的任何其他任意点重置限制。【参考方案5】:

有几种方法可以解决这个问题:

如果总体数据量非常小,则将数据从套接字加载到缓冲区(字节数组、字节缓冲区,取决于您的喜好),然后将 BufferedReader 包裹在内存中的数据周围(通过 ByteArrayInputStream 等) ; 如果发生 OutOfMemoryError,只需捕获它;捕获这个错误一般是不可靠的,但是在捕获数组分配失败的特定情况下,它基本上是安全的(但不能解决一个线程从堆中分配大量可能对其他线程产生的任何连锁反应的问题例如,在您的应用程序中运行); 实现一个只读取这么多字节的包装器 InputStream,然后将其插入到套接字和 BufferedReader 之间; 抛弃 BufferedReader 并通过正则表达式框架拆分行(实现一个 CharSequence,其字符从流中提取,然后定义一个限制行长的正则表达式);原则上,CharSequence 应该是随机访问,但对于简单的“行拆分”正则表达式,实际上您可能会发现始终请求连续的字符,这样您就可以在实现中“作弊”。

【讨论】:

【参考方案6】:

BufferedReader中,不要使用String readLine(),而是使用int read(char[] cbuf, int off, int len);然后,您可以使用boolean ready() 来查看您是否得到了所有内容,并使用构造函数String(byte[] bytes, int offset, int length) 将其转换为字符串。

如果您不关心空格,而只想每行有最大字符数,那么 Stephen 提出的建议非常简单,

import java.io.BufferedReader;
import java.io.IOException;

public class BoundedReader extends BufferedReader 

    private final int  bufferSize;
    private       char buffer[];

    BoundedReader(final BufferedReader in, final int bufferSize) 
        super(in);
        this.bufferSize = bufferSize;
        this.buffer     = new char[bufferSize];
    

    @Override
    public String readLine() throws IOException 
        int no;

        /* read up to bufferSize */
        if((no = this.read(buffer, 0, bufferSize)) == -1) return null;
        String input = new String(buffer, 0, no).trim();

        /* skip the rest */
        while(no >= bufferSize && ready()) 
            if((no = read(buffer, 0, bufferSize)) == -1) break;
        

        return input;
    


编辑:这是为了从用户终端读取行。它一直阻塞到下一行,并返回一个bufferSize-bounded String;该行的任何进一步输入都将被丢弃。

【讨论】:

您不能“使用ready() 来查看您是否掌握了所有信息”。那不是它的用途。请参阅 Javadoc。 你是对的;一般来说,这会吃掉一些你实际上可以使用的数据。但是,在一次发送一行数据的情况下,ready():“如果保证下一个read() 保证不会阻塞输入,则为真,否则为假”,这正是您所需要的。 不,这不是“您所需要的”。它告诉您是否有更多数据可供读取而不会阻塞。而不是“你得到了一切”。请参阅 Javadoc。它并没有以任何方式回答关于无限行长的问题。 您能否举例说明“没有更多数据没有阻塞可用”和“您已到达最后一行”之间的区别? “不阻塞”表示数据已经在缓冲区中。这并不意味着流已关闭,并且稍后可能(不)进入更多数据。示例包括缓慢的网络连接或从用户终端读取数据。

以上是关于Java 中 BufferedReader.readLine() 的最大行长?的主要内容,如果未能解决你的问题,请参考以下文章

java中native的用法

java中Color的用法!

java中重载有啥用

java中int 和 Integer 有啥区别

java 中getBounds啥作用

java中怎么取出数组中的数组