Я хотел бы знать, каковы методы mark()
и reset()
BufferedReader
? Как мне их использовать? Я читал Javadoc, но как новичок не смог его понять.
Что такое пометка и сброс в BufferedReader?
Ответы (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
.
mark()
отмечает определенную точку в потоке, а reset()
сбрасывает поток до самой последней отметки. Эти методы предоставляют функцию book-marking
, которая позволяет вам читать вперед в потоке для проверки предстоящих данных.
Из этой документации:
Метод mark() помечает позицию во входных данных, до которой можно "сбросить" поток, вызвав метод reset(). Параметр readLimit — это количество символов, которые можно прочитать из потока после установки метки до того, как метка станет недействительной. Например, если mark() вызывается с пределом чтения 10, то, когда 11 символов данных считываются из потока до вызова метода reset(), метка становится недействительной, и экземпляр объекта потока не требуется для помни отметку. Обратите внимание, что количество символов, которые можно запомнить с помощью этого метода, может превышать размер внутреннего буфера чтения. Это также не зависит от подчиненного потока, поддерживающего функциональные возможности пометки/сброса.
В документации 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
reset()
? Я думал, что если у вас mark()
с лимитом чтения 0, как только будет прочитан один символ, маркер становится недействительным и нельзя вызвать сброс. Можете ли вы объяснить свой ответ?
- person snooze92; 24.02.2014
markLimit
в методе mark
. У вас всегда будет один и тот же результат. Также см. похожие примеры: 1, 2. На самом деле я хотел задать вопросы по SO, чтобы кто-то дал мне объяснение по этому поводу.
- person Anton Dozortsev; 24.02.2014
mark(readAheadLimit)
/reset()
. По сути, метод mark
просто отмечает точку в текущем буфере, а reset
позволяет вернуться к этой отмеченной точке. Дело в том, что он не предназначен для пометки точки в файле или потоке, так как ему необходимо увеличить размер буфера, чтобы сохранить возможность доступа к отмеченной точке. Вот почему следует использовать предел, относительно меньший, чем размер буфера.
- person snooze92; 25.02.2014
Интерфейс читалки не позволяет вернуться, можно просто читать. BufferedReader, с другой стороны, создает буфер, так что вы можете немного вернуться при чтении. И для этого и нужны эти методы.
С помощью метода mark() вы помещаете «маркер» в позицию, после чего можете читать дальше. Как только вы поймете, что хотите вернуть отмеченную позицию, вы используете для этого reset(). И с этого момента вы снова читаете те же значения. Вы можете использовать его для чего угодно.
Представьте, что у вас есть следующие символы в BufferReader = "123456789", если вы отметите позицию 4 относительно символа "5", а затем сбросите свой BufferReader, вы получите 12345.
Вот пример.
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
вот хорошая ссылка.