Подтвердить что ты не робот

Максимальная длина строки для BufferedReader.readLine() в Java?

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

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

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

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

4b9b3361

Ответ 1

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

Или даже проще, повторно используйте код этот BoundedBufferedReader класс.

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

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

Ответ 2

Другим вариантом является Apache Commons BoundedInputStream:

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

Вокруг этого есть несколько способов:

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

Ответ 6

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