2015-01-28 1 views
1

내 응용 프로그램은 텍스트 파일을 한 줄씩 읽고 파일 끝까지 각 줄의 오프셋을 기록합니다. offset은 readLine이 처음 실행될 때만 변경됩니다. 그 이후로는 더 이상 바뀌지 않습니다. bufferSize를 10에서 16384로 테스트했습니다. 코드에 어떤 문제가 있습니까? 파일이 크면 seek()가 skip()보다 빠르기 때문에 FileInputStream 대신 RandomAccessFile을 사용합니다. 당신이 RandomAccessFile의 객체의 일부인 read() 방법을 사용할 필요가 RandomAccessFile에서 파일 포인터를 업데이트하기 위해BufferedReader의 readLine은 버퍼 크기가 작더라도 파일 포인터를 변경하지 않습니다.

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; 
    ……………………………… 
} 
+0

왜 readLine이 filePointer에 영향을 주어야한다고 생각합니까? filePointer는 아마도 RandomAccessFile에 캡슐화되어 있으며 래퍼 판독기와 관련이 없습니다. – aioobe

+0

어떻게 관련시킬 수 있습니까? – user3152056

+0

문제의 클래스가 관련 인터페이스를 노출하는지 의심 스럽습니다. – aioobe

답변

2

.

별도의 Reader를 만들면 업데이트되지 않습니다.

자신의 InputStream 구현에 당신이 항상 RandomAccessFile에 포장 할 수 BufferedReader 사용해야하는 경우

그래서 RandomAccessFile의에서 읽을 수있는 inputStream을 위임 읽습니다 : 전에이 일을 했어

. 그것은 어렵지 않다 : 내도 버퍼 크기를 설정하여 RandomAccessFileInputStream하고 문제가, 어떤 이유로 BufferedReader 여전히 버퍼링되어 있으므로 파일 포인터를 사용하여 코드 예제를 실행하려고했습니다

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(); 
    } 
} 
} 

편집 기본이되는 inputStream가 읽힐 때마다 8912 씩 증가합니다. 버퍼링이 예상대로 작동하더라도 버퍼는 항상 다음 줄을지나 읽으며 따라서 offset은 줄 끝의 위치가되지 않습니다.

데이터를 버퍼링하지 않고 선을 읽는 자체 구현을 작성하지 않으려는 경우. 더 이상 사용되지 않는 readLine() 메소드가있는 DataInputStream을 사용할 수 있습니다. 이 메소드는 "바이트를 문자로 제대로 변환하지 못하기"때문에 더 이상 사용되지 않지만 ASCII 문자를 사용하는 경우에는 잘되어야합니다.

InputStream in = new RandomAccessFileInputStream(raf); 
DataInputStream dataIn = new DataInputStream(in)) 

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

예상대로 작동합니다. 오프셋은 각 행의 정확한 바이트 수만 업데이트합니다. 그러나 버퍼링되지 않았으므로 파일을 읽는 속도가 느려집니다.

+0

코드를 어떻게 사용하는지 모르겠습니다. 내가 바꾼 것은 FileInputStream에서 = 새로운 FileInputStream (raf.getFD()); ~ RandomAccessFileInputStream = new RandomAccessFileInputStream (raf);입니다. 그러나 결과는 같습니다. – user3152056

+0

BufferedReader를 사용하여 문제가 발생했습니다. 내 대답을 업데이트했습니다. – dkatzel

+0

파일이 ASCII 파일이 아니기 때문에 DataInputStream을 사용할 수 없습니다. getFD() 대신 FileChannel을 사용하고 line.getBytes (encoding) .length를 사용하여 오프셋을 계산했습니다. 그것은 작동합니다. 어쨌든 고맙습니다. – user3152056

관련 문제