BufferedReader 中的标记和重置是啥?

Posted

技术标签:

【中文标题】BufferedReader 中的标记和重置是啥?【英文标题】:What are mark and reset in BufferedReader?BufferedReader 中的标记和重置是什么? 【发布时间】:2012-01-04 14:52:50 【问题描述】:

我想知道BufferedReadermark()reset()方法是什么?我该如何使用它们?我阅读了 Javadoc,但作为初学者我无法理解它。

【问题讨论】:

【参考方案1】:

流的markreset 方法提供了一种在流中向后跳转并重新读取数据的方法。

当您在BufferedReader 上调用mark() 时,它将开始将您从该点读取的数据向前保存在其内部缓冲区中。当您调用reset() 时,它将跳回到流的标记位置,因此下一个read()s 将由内存缓冲区满足。当您读取该缓冲区的末尾时,它将无缝返回读取新数据。 BufferedInputStream 的工作方式相同。

mark 的 int 参数告诉它您希望能够后退的最大字符数(对于 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() 方法,但StringReaders 已经支持markreset!从 内存中 数据源读取的流通常支持 markreset 本身,因为它们已经在内存中拥有所有数据,因此它们很容易再次读取。从文件或管道或网络套接字读取的流自然不支持markreset,但您始终可以将该功能添加到任何流中,方法是将其包装在BufferedInputStreamBufferedReader 中。

【讨论】:

【参考方案2】:

mark() 标记流中的特定点,reset() 将流重置为最新标记。这些方法提供了book-marking 功能,允许您在流中提前读取以检查即将出现的数据。

来自documentation:

ma​​rk() 方法在输入中标记一个位置,流可以通过以下方式“重置”到该位置 调用 reset() 方法。参数readLimit是个数 之前设置标记后可以从流中读取的字符 标记无效。例如,如果使用 read 调用 mark() 限制为 10,然后从流中读取 11 个字符的数据 在调用 reset() 方法之前,标记无效并且 流对象实例不需要记住标记。注意 这种方法可以记住的字符数可以是 大于内部读取缓冲区的大小。它也不是 依赖于支持标记/重置的从属流 功能。

【讨论】:

你也可以用它来简单地标记开始并回到开始重新读取缓冲区吗?例如,如果您想读取文件两次。一次获得上下文,回到开头,然后再读一遍? 它的参数是什么?【参考方案3】:

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,则一旦读取一个字符,标记就会变得无效并且无法调用重置。你能解释一下你的答案吗? @snooze92 只是为了运行这个示例并尝试更改mark 方法中的markLimit 参数。您将始终得到相同的结果。另请参阅类似示例:1、2。我实际上想对 SO 提出问题,有人给了我一个解释。 在运行了更多我自己的示例之后,我对mark(readAheadLimit)/reset() 机制有了更好的理解。基本上,mark 方法只是在 当前缓冲区 中标记一个点,reset 让您回到那个标记点。问题是,它并不意味着在文件或流中标记点,因为它需要增加缓冲区大小以保持访问标记点的可能性。这就是为什么一个应该使用一个相对小于缓冲区大小的限制。 截至 “为什么示例在限制为 0(或任何限制)的情况下工作” 也很容易解释。默认缓冲区大小(在两个示例中都使用)是 4096。示例中使用的字符串非常小,以至于一旦创建 BufferedReader,它就会在第一个缓冲区中完全读取。稍后它不会再读任何内容,因此根本没有使用 limit 参数......这使得这个例子变得毫无意义和令人困惑的恕我直言。稍微更改示例以在 BufferedReader 构造函数中添加非常小的缓冲区大小(例如 2 或 3)将开始引发无效标记异常。 @snooze92 感谢您的解释。为您的 cmets 点赞。【参考方案4】:

阅读器界面让你返回,你可以阅读。另一方面,BufferedReader 创建一个缓冲区,因此您可以在读取时返回一点。这就是这些方法的用途。

使用 mark() 方法,您可以在某个位置上放置一个“标记”,然后您可以继续阅读。一旦你意识到你想要返回你使用 reset() 的标记位置。从那时起,您再次读取相同的值。你可以用它做任何你想做的事情。

【讨论】:

【参考方案5】:

假设您在 BufferReader = "123456789" 中有以下字符,如果您在相对于 '5' 字符的位置 4 进行标记,然后重置 BufferReader,您将得到 12345。

【讨论】:

这是一半的答案,什么是重置? 嘿给出了正确的答案。重置只是将指向要读取的字符的指针带到它被标记的位置。【参考方案6】:

这是一个例子。

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、here's 来说是一个很好的参考。

【讨论】:

以上是关于BufferedReader 中的标记和重置是啥?的主要内容,如果未能解决你的问题,请参考以下文章

请问JAVA中的BufferedReader是啥意思?

尽管 Angular 中的表单已重置,但输入字段仍标记为红色

将文件重置为特定提交的命令是啥? [复制]

Java Logging 框架中的标记是啥,使用它们的原因是啥?

输入流,标记(),重置()

Java I/O 中的“Stream”和“Buffer”到底是啥意思?