Максимальная длина строки для BufferedReader.readLine () в Java? - PullRequest
22 голосов
/ 11 мая 2011

Я использую метод BufferedReader readLine() для чтения строк текста из сокета.

Нет очевидного способа ограничить длину прочитанной строки.

Я обеспокоен тем, что источник данных может (злонамеренно или по ошибке) записать много данных без каких-либо символов перевода строки, и это приведет к тому, что BufferedReader выделит неограниченный объем памяти.

Есть ли способ избежать этого? Или я должен сам реализовать ограниченную версию readLine()?

Ответы [ 6 ]

12 голосов
/ 11 мая 2011

Самый простой способ сделать это - реализовать свой собственный считыватель ограниченных строк.

Или, еще проще, повторно использовать код из this BoundedBufferedReader class .

На самом деле, кодирование readLine(), которое работает так же, как и стандартный метод, не является тривиальным.Работа с тремя типами ограничителя строки ПРАВИЛЬНО требует некоторого довольно осторожного кодирования.Интересно сравнить различные подходы по вышеуказанной ссылке с версией Sun и версией Apache Harmony BufferedReader.

Примечание: я не совсем уверен, что ограниченная версия или версия Apache на 100% верна.Ограниченная версия предполагает, что базовый поток поддерживает метку и сброс, что, конечно, не всегда верно.Версия Apache, по-видимому, опережает чтение одного символа, если она видит CR как последний символ в буфере.Это сломало бы MacOS при чтении ввода, введенного пользователем.Версия Sun решает эту проблему, устанавливая флаг, позволяющий пропустить возможный LF после CR при следующей операции read...;т.е. нет ложного чтения вперед.

11 голосов
/ 20 января 2013

Другой вариант - Apache Commons ' BoundedInputStream :

InputStream bounded = new BoundedInputStream(is, MAX_BYTE_COUNT);
BufferedReader reader = new BufferedReader(new InputStreamReader(bounded));
String line = reader.readLine();
3 голосов
/ 11 мая 2011

Возможно, самое простое решение - использовать немного другой подход. Вместо того, чтобы пытаться предотвратить DoS путем ограничения одного конкретного чтения, ограничьте весь объем чтения необработанных данных. Таким образом, вам не нужно беспокоиться об использовании специального кода для каждого отдельного чтения и цикла, если объем выделяемой памяти пропорционален входящим данным.

Вы можете измерить Reader или, возможно, более подходящим образом, некодированный Stream или эквивалентный.

2 голосов
/ 11 мая 2011

Предел для строки составляет 2 миллиарда символов. Если вы хотите, чтобы ограничение было меньше, вам нужно прочитать данные самостоятельно. Вы можете читать по одному символу за раз из буферизованного потока, пока не будет достигнут предел или символ новой строки.

1 голос
/ 11 мая 2011

Есть несколько способов обойти это:

  • если общий объем данных очень мал, загрузите данные из сокета в буфер (байтовый массив, байтовый буфер, в зависимости от того, что вы предпочитаете), затем оберните BufferedReader вокруг данных в памяти (через ByteArrayInputStream и т. Д.) ;
  • просто перехватите OutOfMemoryError, если это произойдет; перехват этой ошибки, как правило, ненадежен, но в конкретном случае перехвата ошибок выделения массива он в основном безопасен (но не решает проблему какого-либо эффекта привязки, который один поток, выделяющий большие объемы из кучи, мог бы иметь в других потоках работает в вашем приложении, например);
  • реализовать оболочку InputStream, которая будет считывать только столько байтов, а затем вставлять ее между сокетом и BufferedReader;
  • ditch BufferedReader и разделение ваших строк с помощью платформы регулярных выражений (реализуйте CharSequence, чьи символы извлекаются из потока, а затем определяйте регулярное выражение, ограничивающее длину строк); в принципе, CharSequence должен быть произвольным доступом, но для простого регулярного выражения "разбиение строки" на практике вы, вероятно, обнаружите, что последовательные символы всегда запрашиваются, так что вы можете "обмануть" в своей реализации.
0 голосов
/ 01 декабря 2014

В BufferedReader вместо String readLine() используйте int read(char[] cbuf, int off, int len);затем вы можете использовать boolean ready(), чтобы увидеть, все ли у вас получилось, и конвертировать в строку, используя конструктор String(byte[] bytes, int offset, int length).

Если вам не нужны пробелы и вы просто хотите получить максимумколичество символов в строке, то предложение, предложенное Стивеном, действительно простое:

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 -ограниченный String;любой дальнейший ввод в строке отбрасывается.

...