Что такое пометка и сброс в BufferedReader?

Я хотел бы знать, каковы методы mark() и reset() BufferedReader? Как мне их использовать? Я читал Javadoc, но как новичок не смог его понять.


person saurabh ranu    schedule 23.11.2011    source источник


Ответы (6)


Методы потоков mark и reset обеспечивают возможность перехода назад по потоку и повторного чтения данных.

Когда вы вызываете mark() для BufferedReader, он начнет хранить данные, которые вы читаете с этой точки, в своем внутреннем буфере. Когда вы вызываете reset(), он возвращается к отмеченной позиции потока, поэтому следующие read() будут удовлетворяться буфером в памяти. Когда вы прочитаете конец этого буфера, он плавно вернется к чтению свежих данных. BufferedInputStream работает так же.

Параметр int для mark указывает максимальное количество символов (для BufferedReader) или байтов (для BufferedInputStream), которое вы хотите иметь возможность вернуться назад. Если вы прочитаете слишком много данных за отмеченной позицией, то метка может быть "аннулирована", и вызов reset() завершится ошибкой с исключением.

Небольшой пример:

BufferedReader r = new BufferedReader(new StringReader(
    "Happy Birthday to You!\n" +
    "Happy Birthday, dear " + System.getProperty("user.name") + "!"));
r.mark(1000); // save the data we are about to read
System.out.println(r.readLine()); // read the first line
r.reset(); // jump back to the marked position
r.mark(1000); // start saving the data again
System.out.println(r.readLine()); // read the first line again
System.out.println(r.readLine()); // read the second line
r.reset(); // jump back to the marked position
System.out.println(r.readLine()); // read the first line one final time

В этом примере я завернул StringReader в BufferedReader, чтобы получить метод readLine(), но StringReader уже поддерживают mark и reset сами по себе! Потоки, которые считываются из находящегося в памяти источника данных, обычно сами поддерживают mark и reset, поскольку они уже содержат все данные в памяти, поэтому им легко прочитать их снова. Потоки, которые считываются из файлов, каналов или сетевых сокетов, естественно не поддерживают mark и reset, но вы всегда можете добавить эту функцию к любому потоку, заключив его в BufferedInputStream или BufferedReader.

person Boann    schedule 23.11.2011

mark() отмечает определенную точку в потоке, а reset() сбрасывает поток до самой последней отметки. Эти методы предоставляют функцию book-marking, которая позволяет вам читать вперед в потоке для проверки предстоящих данных.

Из этой документации:

Метод mark() помечает позицию во входных данных, до которой можно "сбросить" поток, вызвав метод reset(). Параметр readLimit — это количество символов, которые можно прочитать из потока после установки метки до того, как метка станет недействительной. Например, если mark() вызывается с пределом чтения 10, то, когда 11 символов данных считываются из потока до вызова метода reset(), метка становится недействительной, и экземпляр объекта потока не требуется для помни отметку. Обратите внимание, что количество символов, которые можно запомнить с помощью этого метода, может превышать размер внутреннего буфера чтения. Это также не зависит от подчиненного потока, поддерживающего функциональные возможности пометки/сброса.

person kv-prajapati    schedule 23.11.2011
comment
Вы также можете просто использовать это, чтобы просто отметить начало и вернуться к началу, чтобы перечитать буфер? например, если вы хотите прочитать файл дважды. один раз, чтобы получить контекст, вернуться к началу, а затем прочитать снова? - person anon58192932; 30.01.2015
comment
а что у него с параметром? - person Asif Mushtaq; 06.04.2016

В документации Reader::mark(int readLimit) говорится:

Устанавливает позицию метки в этом считывателе. Параметр readLimit указывает, сколько символов можно прочитать, прежде чем метка станет недействительной. Вызов reset() переместит считыватель обратно в отмеченную позицию, если readLimit не был превышен.

Пример:

import java.io.*;
import static java.lang.System.out;

public class App {

    public static final String TEST_STR = "Line 1\nLine 2\nLine 3\nLine 4\n";

    public static void main(String[] args) {

        try (BufferedReader in = new BufferedReader(new StringReader(TEST_STR))) {

            // first check if this Reader support mark operation
            if (in.markSupported()) {

                out.println(in.readLine());
                in.mark(0);                     // mark 'Line 2'
                out.println(in.readLine());
                out.println(in.readLine());
                in.reset();                     // reset 'Line 2'
                out.println(in.readLine());
                in.reset();                     // reset 'Line 2'
                out.println(in.readLine());
                in.mark(0);                     // mark 'Line 3'
                out.println(in.readLine());
                in.reset();                     // reset 'Line 3'
                out.println(in.readLine());
                out.println(in.readLine());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Выход:

Line 1
Line 2
Line 3
Line 2
Line 2
Line 3
Line 3
Line 4
person Anton Dozortsev    schedule 19.01.2014
comment
Как вы можете позвонить reset()? Я думал, что если у вас mark() с лимитом чтения 0, как только будет прочитан один символ, маркер становится недействительным и нельзя вызвать сброс. Можете ли вы объяснить свой ответ? - person snooze92; 24.02.2014
comment
@ snooze92 Просто чтобы запустить этот пример и попытаться изменить аргумент markLimit в методе mark. У вас всегда будет один и тот же результат. Также см. похожие примеры: 1, 2. На самом деле я хотел задать вопросы по SO, чтобы кто-то дал мне объяснение по этому поводу. - person Anton Dozortsev; 24.02.2014
comment
После запуска большего количества собственных примеров я немного лучше понимаю механизм mark(readAheadLimit)/reset(). По сути, метод mark просто отмечает точку в текущем буфере, а reset позволяет вернуться к этой отмеченной точке. Дело в том, что он не предназначен для пометки точки в файле или потоке, так как ему необходимо увеличить размер буфера, чтобы сохранить возможность доступа к отмеченной точке. Вот почему следует использовать предел, относительно меньший, чем размер буфера. - person snooze92; 25.02.2014
comment
Что касается почему пример работает с пределом 0 (или любым пределом на самом деле), также довольно легко объяснить. Размер буфера по умолчанию (используемый в обоих примерах) равен 4096. Строка, используемая в примерах, настолько мала, что полностью считывается в первом буфере, как только создается BufferedReader. Позже он больше ничего не читает, поэтому параметр limit вообще не используется ... что делает пример довольно бессмысленным и запутанным ИМХО. Немного изменив примеры, чтобы добавить очень маленький размер буфера (например, 2 или 3) в конструкторах BufferedReader, вы начнете выдавать недопустимые исключения меток. - person snooze92; 25.02.2014
comment
@ snooze92 Спасибо за объяснение. Проголосовал за ваши комментарии. - person Anton Dozortsev; 25.02.2014

Интерфейс читалки не позволяет вернуться, можно просто читать. BufferedReader, с другой стороны, создает буфер, так что вы можете немного вернуться при чтении. И для этого и нужны эти методы.

С помощью метода mark() вы помещаете «маркер» в позицию, после чего можете читать дальше. Как только вы поймете, что хотите вернуть отмеченную позицию, вы используете для этого reset(). И с этого момента вы снова читаете те же значения. Вы можете использовать его для чего угодно.

person lzap    schedule 23.11.2011

Представьте, что у вас есть следующие символы в BufferReader = "123456789", если вы отметите позицию 4 относительно символа "5", а затем сбросите свой BufferReader, вы получите 12345.

person Mikhaél Santos    schedule 23.11.2011
comment
Это половина ответа, а что сбрасывать? - person eeerahul; 23.11.2011
comment
Эй дал правильный ответ. Сброс просто переводит указатель, указывающий на символ, который нужно прочитать, в положение, в котором он был отмечен. - person Umang Desai; 16.10.2013

Вот пример.

int bufferSize = 4;
int readLimit = 4
ByteArrayInputStream byteInputStream = new ByteArrayInputStream("123456789abcdef".getBytes());
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(byteInputStream, bufferSize)) {
        bufferedInputStream.mark(readLimit);
        System.out.print((char) bufferedInputStream.read());//byte1
        System.out.print((char) bufferedInputStream.read());//byte2
        System.out.print((char) bufferedInputStream.read());//byte3
        System.out.print((char) bufferedInputStream.read());//byte4
        bufferedInputStream.reset();
        System.out.print((char) bufferedInputStream.read());//byte5
        // Using this next reset() instead of the first one will throw an exception
        // bufferedInputStream.reset();

        System.out.print((char) bufferedInputStream.read());
        System.out.print((char) bufferedInputStream.read());
        System.out.print((char) bufferedInputStream.read());
    }

Выход: 12341234

Для целей readLimit вот хорошая ссылка.

person Orvyl    schedule 04.08.2018