读取到文件结束后,BufferedReader重置失败 [英] BufferedReader reset fail after read to end of file

查看:173
本文介绍了读取到文件结束后,BufferedReader重置失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个BufferedReader包装在一个文件上,我想标记一个地方,然后使用reset()返回到这个位置。我已经阅读了java api,它声明了mark(readlimit),当读取太多字节时,重置将失败。所以我想我可以设置一个很大的限制。

I have a BufferedReader wrapped on a file, I want to mark a place and use reset() later to get back to this position. I have read the java api, it states mark (readlimit), when read too many bytes, the reset will fail. So i figure I can set a large limit.

但是,如果我有代码

BufferedReader br=...;
br.mark(1024000); // large but not as large as file. 
while(br.ready()){
    //keep reading. to find some stuff. 
}
//now the br.ready() is false
br.reset() // It will fail with mark invalid exception

我认为问题是当br到达文件末尾时,br不再准备好,并且重置失败....我可以设法继续阅读直到最后一行并停止,但那我该怎么做?

I think the problem is when br reach end of file, the br is no longer ready, and reset fails....I can manage to keep reading until the 2nd last line and stop, but then how do I do that?

我发现一个丑陋的解决方案是使用PushbackReader,以保存我阅读和推送的所有内容在while循环之后回来。我想知道是否有更好的解决方案。

I found that an ugly solution would be using PushbackReader, to save all the stuff I read and push back after the while loop. I am wondering if there's a better solution.

推荐答案

我想你错过了 mark()的文档,其中明确说明了

I think you missed the documentation of mark() where it clearly states


参数:

readAheadLimit - 限制在保留标记的同时可以读取的字符数。读取这么多字符后,尝试重置流可能会失败。

Parameters:
readAheadLimit - Limit on the number of characters that may be read while still preserving the mark. After reading this many characters, attempting to reset the stream may fail.

所以如果你想完全读取流和 reset()之后你需要调用 mark(),其参数至少与你文件的其余部分一样大。

so if you want to fully read the stream and reset() afterwards you need to call mark() with a parameter that is as least as big as the rest of your file.

但是作为 BufferedReader.html #mark(int)添加


限制值大于输入缓冲区的大小将导致分配一个大小不小于限制的新缓冲区。因此,应谨慎使用大值。

A limit value larger than the size of the input buffer will cause a new buffer to be allocated whose size is no smaller than limit. Therefore large values should be used with care.

因此,如果需要关注内存,请考虑是否可以合并搜索和其他处理步骤或者在两个步骤之间重新打开源。当然还有一种方法可以利用 FileChannel 能够自由搜索任何给定文件,但不会为您提供字符或字符串。

So if memory is a concern, consider if you can incorporate the search and other processing steps or reopen the source between both steps. Surely there is also a way to utilize FileChannel which has the ability to freely seek through any given file, but won't provide you with characters or strings.

您可以使用此类的getReadCharacters() reopenAt(BigInteger)(没有经过适当测试的直接替换 BufferedReader s对文件采取行动):

You can maybe utilize getReadCharacters() and reopenAt(BigInteger) of this class (not properly tested drop-in replacement for BufferedReaders acting upon files):

import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;

/**
 * Created by TheConstructor for http://stackoverflow.com/a/24620470/1266906.
 */
public class MarkableFileReader extends Reader {
    /**
     * Cached instance of {@link java.math.BigInteger} of value
     * {@link Long#MAX_VALUE} (used in {@link #skip(java.math.BigInteger)})
     */
    public static final BigInteger LONG_MAX_VALUE                    = BigInteger.valueOf(Long.MAX_VALUE);
    /**
     * Default value of {@link #reopenOnResetThreshold} (10 MiB)
     */
    public static final int        DEFAULT_REOPEN_ON_RESET_THRESHOLD = 10 * 1024 * 1024;
    /**
     * Initialize the line-reading-buffer to this size
     */
    public static final int        EXPECTED_LINE_LENGTH              = 80;

    private final File           file;
    private final Charset        charset;
    private       BufferedReader reader;
    private       BigInteger     readCharacters;
    private       BigInteger     mark;
    private       boolean        reopenOnReset;
    private final int            reopenOnResetThreshold;
    private final BigInteger     reopenOnResetThresholdBI;
    /**
     * {@link java.io.BufferedReader#readLine()} is implemented to skip the
     * {@code '\n'} of an {@code "\r\n"} only with the next read. The same
     * behaviour is implemented here.
     */
    private       boolean        skipLf;
    private       boolean        skipLfMark;

    public MarkableFileReader(String fileName) throws FileNotFoundException {
        this(fileName, null);
    }

    public MarkableFileReader(String fileName, Charset charset) throws FileNotFoundException {
        this(fileName, charset, DEFAULT_REOPEN_ON_RESET_THRESHOLD);
    }

    public MarkableFileReader(String fileName, Charset charset, int reopenOnResetThreshold)
            throws FileNotFoundException {
        this(new File(fileName), charset, reopenOnResetThreshold);
    }

    public MarkableFileReader(File file) throws FileNotFoundException {
        this(file, null, DEFAULT_REOPEN_ON_RESET_THRESHOLD);
    }

    public MarkableFileReader(File file, Charset charset, int reopenOnResetThreshold) throws FileNotFoundException {
        super();
        this.file = file;
        this.charset = charset;
        this.mark = null;
        this.skipLfMark = false;
        this.reopenOnReset = false;
        this.reopenOnResetThreshold = Math.max(0, reopenOnResetThreshold);
        this.reopenOnResetThresholdBI = BigInteger.valueOf(this.reopenOnResetThreshold);
        initReader();
    }

    private void initReader() throws FileNotFoundException {
        final FileInputStream fileInputStream = new FileInputStream(file);
        final InputStreamReader inputStreamReader = (charset == null) ?
                                                    new InputStreamReader(fileInputStream) :
                                                    new InputStreamReader(fileInputStream, charset);
        reader = new BufferedReader(inputStreamReader);
        this.readCharacters = BigInteger.ZERO;
        this.reopenOnReset = true;
        this.skipLf = false;
    }

    private void incrementReadCharacters() {
        this.readCharacters = this.readCharacters.add(BigInteger.ONE);
    }

    private void incrementReadCharacters(final long characters) {
        if(characters != -1) {
            this.readCharacters = this.readCharacters.add(BigInteger.valueOf(characters));
        }
    }

    @Override
    public int read() throws IOException {
        synchronized (lock) {
            final int read = reader.read();
            if (read != -1) {
                incrementReadCharacters();
            }
            if (skipLf && read == '\n') {
                skipLf = false;
                return read();
            }
            return read;
        }
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        synchronized (lock) {
            if ((off < 0) || (len < 0) ||
                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
            if(skipLf) {
                int firstChar = read();
                if (firstChar == -1) {
                    return 0;
                }
                cbuf[off] = (char) firstChar;
                if (len > 1) {
                    final int read = reader.read(cbuf, off + 1, len - 1);
                    incrementReadCharacters(read);
                    return read + 1;
                } else {
                    return 1;
                }
            } else {
                final int read = reader.read(cbuf, off, len);
                incrementReadCharacters(read);
                return read;
            }
        }
    }

    /**
     * Reads a line of text.  A line is considered to be terminated by any one
     * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
     * followed immediately by a linefeed.
     * <p>Note: this is not directly proxied to
     * {@link java.io.BufferedReader#readLine()} as we need to know how many
     * characters compose the line-ending for {@link #getReadCharacters()} to
     * return correct numbers</p>
     *
     * @return A String containing the contents of the line, not including
     * any line-termination characters, or null if the end of the
     * stream has been reached
     * @throws IOException
     *         If an I/O error occurs
     * @see java.nio.file.Files#readAllLines(java.nio.file.Path, java.nio.charset.Charset)
     * @see java.io.BufferedReader#readLine()
     */
    public String readLine() throws IOException {
        synchronized (lock) {
            final CharArrayWriter charArrayWriter = new CharArrayWriter(EXPECTED_LINE_LENGTH);
            int lastRead = read();
            if(lastRead == -1) {
                return null;
            }
            while (lastRead != -1 && lastRead != '\r' && lastRead != '\n') {
                charArrayWriter.write(lastRead);
                lastRead = read();
            }
            if(lastRead == '\r') {
                skipLf = true;
            }
            return charArrayWriter.toString();
        }
    }

    @Override
    public long skip(long n) throws IOException {
        if (n < 0L) {
            throw new IllegalArgumentException("skip value is negative");
        }
        if(n == 0L) {
            return 0L;
        }
        synchronized (lock) {
            if(skipLf) {
                int read = read();
                if (read == -1) {
                    return 0;
                }
                final long skip = reader.skip(n - 1);
                incrementReadCharacters(skip);
                return skip + 1;
            } else {
                final long skip = reader.skip(n);
                incrementReadCharacters(skip);
                return skip;
            }
        }
    }

    @Override
    public boolean ready() throws IOException {
        synchronized (lock) {
            return reader.ready();
        }
    }

    @Override
    public boolean markSupported() {
        return true;
    }

    @Override
    public void mark(int readAheadLimit) throws IOException {
        if(readAheadLimit < 0) {
            throw new IllegalArgumentException("readAheadLimit needs to be 0 or greater");
        }
        synchronized (lock) {
            mark = readCharacters;
            skipLfMark = skipLf;
            reopenOnReset = false;
            if (reader.markSupported()) {
                if (readAheadLimit >= reopenOnResetThreshold) {
                    reader.mark(reopenOnResetThreshold);
                } else {
                    reader.mark(readAheadLimit);
                }
            }
        }
    }

    @Override
    public void reset() throws IOException {
        synchronized (lock) {
            if (mark == null) {
                throw new IOException("call mark() first");
            }
            final BigInteger readSinceMark = readCharacters.subtract(mark);
            if (reopenOnReset ||
                    readSinceMark.compareTo(reopenOnResetThresholdBI) >= 0 ||
                    !reader.markSupported()) {
                if (!reopenAt(mark)) {
                    throw new IOException("reopening at position failed");
                }
            } else {
                reader.reset();
                readCharacters = mark;
            }
            skipLf = skipLfMark;
        }
    }

    @Override
    public void close() throws IOException {
        synchronized (lock) {
            reader.close();
        }
    }

    public BigInteger getReadCharacters() {
        synchronized (lock) {
            return readCharacters;
        }
    }

    public boolean reopenAt(final BigInteger position) throws IOException {
        synchronized (lock) {
            if (reader != null) {
                reader.close();
            }
            initReader();
            BigInteger skip = skip(position);
            return skip.equals(position);
        }
    }

    public BigInteger skip(final BigInteger n) throws IOException {
        synchronized (lock) {
            BigInteger remaining = n;
            while (remaining.compareTo(BigInteger.ZERO) > 0) {
                long skip = skip(remaining.min(LONG_MAX_VALUE).longValue());
                remaining = remaining.subtract(BigInteger.valueOf(skip));
                if (skip < 1) {
                    break;
                }
            }
            return n.subtract(remaining);
        }
    }
}

这篇关于读取到文件结束后,BufferedReader重置失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆