Java 即使缓冲区大小很小,BufferedReader的readLine也不会更改文件指针

Java 即使缓冲区大小很小,BufferedReader的readLine也不会更改文件指针,java,android,bufferedreader,randomaccessfile,Java,Android,Bufferedreader,Randomaccessfile,我的应用程序逐行读取文本文件,并记录每行的偏移量,直到文件结束。偏移量仅在首次执行readLine时更改。从那以后就再也没有变化了。我测试了从10到16384的缓冲区大小。我的代码有什么问题?我使用RandomAccessFile而不是FileInputStream,因为当文件较大时,seek()比skip()快 String line; long offset; RandomAccessFile raf = new RandomAccessFile("data.txt", "

我的应用程序逐行读取文本文件,并记录每行的偏移量,直到文件结束。偏移量仅在首次执行readLine时更改。从那以后就再也没有变化了。我测试了从10到16384的缓冲区大小。我的代码有什么问题?我使用RandomAccessFile而不是FileInputStream,因为当文件较大时,seek()比skip()快

String line;        
long offset;

RandomAccessFile raf = new RandomAccessFile("data.txt", "r");
FileInputStream is = new FileInputStream(raf.getFD());
InputStreamReader isr = new InputStreamReader(is, encoding);
BufferedReader br = new BufferedReader(isr, bufferSize);

while (true) {
    offset = raf.getFilePointer(); // offset remains the same after 1st readLine. why?
    if ((line = br.readLine()) == null) // line has correct value.
        return;
    ………………………………
}

要更新
RandomAccessFile
中的文件指针,需要使用属于RandomAccessFile对象的
read()
方法

制作一个单独的阅读器不会更新它

如果您需要使用
BufferedReader
,您可以始终在自己的InputStream实现中包装一个RandomAccessFile,以便读取InputStream委托以读取RandomAccessFile:

我以前不得不这么做。这并不难:

public final class RandomAccessFileInputStream extends InputStream{

private final RandomAccessFile randomAccessFile;
private long bytesRead=0;
/**
 * The number of bytes to read in the stream;
 * or {@code null} if we should read the whole thing.
 */
private final Long length;
private final boolean ownFile;
/**
 * Creates a new {@link RandomAccessFileInputStream}
 * of the given file starting at the given position.
 * Internally, a new {@link RandomAccessFile} is created
 * and is seek'ed to the given startOffset
 * before reading any bytes.  The internal 
 * {@link RandomAccessFile} instance is managed by this
 * class and will be closed when {@link #close()} is called.
 * @param file the {@link File} to read.
 * @param startOffset the start offset to start reading
 * bytes from.
 * @throws IOException if the given file does not exist 
 * @throws IllegalArgumentException if the startOffset is less than 0.
 */
public RandomAccessFileInputStream(File file, long startOffset) throws IOException{
    assertStartOffValid(file, startOffset);
    this.randomAccessFile = new RandomAccessFile(file, "r");
    randomAccessFile.seek(startOffset);
    this.length = null;
    ownFile =true;
}
/**
 * Creates a new {@link RandomAccessFileInputStream}
 * of the given file starting at the given position
 * but will only read the given length.
 * Internally, a new {@link RandomAccessFile} is created
 * and is seek'ed to the given startOffset
 * before reading any bytes.  The internal 
 * {@link RandomAccessFile} instance is managed by this
 * class and will be closed when {@link #close()} is called.
 * @param file the {@link File} to read.
 * @param startOffset the start offset to start reading
 * bytes from.
 * @param length the maximum number of bytes to read from the file.
 *  this inputStream will only as many bytes are in the file.
 * @throws IOException if the given file does not exist
 * @throws IllegalArgumentException if either startOffset or length are less than 0
 * or if startOffset < file.length().
 */
public RandomAccessFileInputStream(File file, long startOffset, long length) throws IOException{
    assertStartOffValid(file, startOffset);
    if(length < 0){
        throw new IllegalArgumentException("length can not be less than 0");
    }
    this.randomAccessFile = new RandomAccessFile(file, "r");
    randomAccessFile.seek(startOffset);
    this.length = length;
    ownFile =true;
}
private void assertStartOffValid(File file, long startOffset) {
    if(startOffset < 0){
        throw new IllegalArgumentException("start offset can not be less than 0");
    }

    if(file.length() < startOffset){
        throw new IllegalArgumentException(
                String.format("invalid startOffset %d: file is only %d bytes" ,
                        startOffset,
                        file.length()));
    }
}
/**
 * Creates a new RandomAccessFileInputStream that reads
 * bytes from the given {@link RandomAccessFile}.
 * Any external changes to the file pointer
 * via {@link RandomAccessFile#seek(long)} or similar
 * methods will also alter the subsequent bytes read
 * by this {@link InputStream}.
 * Closing the inputStream returned by this constructor
 * DOES NOT close the {@link RandomAccessFile} which 
 * must be closed separately by the caller.
 * @param file the {@link RandomAccessFile} instance 
 * to read as an {@link InputStream}; can not be null.
 * @throws NullPointerException if file is null.
 */
public RandomAccessFileInputStream(RandomAccessFile file){
    if(file ==null){
        throw new NullPointerException("file can not be null");
    }
    this.randomAccessFile = file;
    length = null;
    ownFile =false;
}

@Override
public synchronized int read() throws IOException {
    if(length !=null && bytesRead >=length){
        return -1;
    }
    int value = randomAccessFile.read();
    if(value !=-1){
        bytesRead++;
    }
    return value;

}

@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
    if(length != null && bytesRead >=length){
        return -1;
    }
    final int reducedLength = computeReducedLength(len);
    int numberOfBytesRead = randomAccessFile.read(b, off, reducedLength);
    bytesRead+=numberOfBytesRead;
    return numberOfBytesRead;
}
private int computeReducedLength(int len) {
    if(length ==null){
        return len;         
    }
    return Math.min(len, (int)(length - bytesRead));
}
/**
 * If this instance was creating
 * using the {@link #RandomAccessFileInputStream(RandomAccessFile)}
 * constructor, then this method does nothing- the RandomAccessFile
 * will still be open.
 * If constructed using {@link #RandomAccessFileInputStream(File, long)}
 * or {@link #RandomAccessFileInputStream(File, long, long)},
 * then the internal {@link RandomAccessFile} will be closed.
 */
@Override
public void close() throws IOException {
    //if we created this randomaccessfile
    //then its our job to close it.
    if(ownFile){
        randomAccessFile.close();
    }
}
}

一切正常。偏移量仅更新每行的确切字节数。但是,由于没有缓冲,读取文件的速度会变慢。

为什么您认为读线会影响文件指针?filePointer可能封装在RandomAccessFile中,与包装器读取器无关。我如何使它们相关?我怀疑相关的类公开了相关的接口。为什么不自己跟踪字节偏移量,例如使用pos+=line.length()?@aioobe,因为Reader.readLine()可能不够用去掉尾随空格,就像换行一样。将读取错误的字节数。另外,假设字符编码是每个字符一个字节,我不知道如何使用你的代码。我更改的是来自FileInputStream的is=newFileInputStream(raf.getFD());to RandomAccessFileInputStream is=新的RandomAccessFileInputStream(raf);。但结果是一样的。我已经更新了我的答案。由于使用了BufferedReaderI,所以出现了一个问题。我无法使用DataInputStream,因为该文件不是ascii文件。我使用FileChannel而不是getFD(),并使用line.getBytes(encoding).length来计算偏移量。它起作用了。无论如何,谢谢你。
InputStream in = new RandomAccessFileInputStream(raf);
DataInputStream dataIn = new DataInputStream(in))

 ...
  if ((line = dataIn.readLine()) == null) 
  ...