2014-07-07 2 views
2

파일에 래핑 된 BufferedReader가 있습니다. 장소를 표시하고이 위치로 돌아 오기 위해 reset()을 나중에 사용하고 싶습니다. 나는 너무 많은 바이트를 읽을 때 자바 API를 읽었는데 마크 (readlimit)를 나타내며, 리셋은 실패 할 것이다. 그래서 나는 큰 한계를 설정할 수 있습니다.파일 끝에 읽은 후 BufferedReader 재설정이 실패합니다.

나는 코드가

나는 BR 파일의 끝에 도달 할 때 문제가 생각
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이 더 이상 준비하고, 리셋이 나는 때까지 읽는 유지 관리 할 수 ​​있습니다 .... 실패가 없다 그러나 경우 마지막 두 번째 줄과 그만,하지만 어떻게해야합니까?

추악한 솔루션은 내가 읽은 모든 것을 저장하고 while 루프 이후에 다시 밀어 넣기 위해 PushbackReader를 사용하는 것으로 나타났습니다. 더 나은 해결책이 있는지 궁금합니다. - 마크를 보관 유지하면서 읽을 수있는 문자의 수에 제한
readAheadLimit :

답변

4

나는 그것을 명확하게

매개 변수를 명시 어디가 documentation of mark()를 놓쳤다 생각합니다. 이 문자를 읽은 후 스트림을 재설정하려고 시도하면 실패 할 수 있습니다.

그래서 당신은 완전히 스트림 reset()을 읽으려면 그 후에 당신은 파일의 나머지 부분만큼 적어도 큰 매개 변수와 함께 mark()를 호출해야합니다.

그러나 BufferedReader.html#mark(int) 설명서에

추가로 입력 버퍼의 사이즈보다 큰 한계 값 크기 한계보다 작되지 할당 될 새로운 버퍼가 발생할

. 따라서 큰 값은 신중히 사용해야합니다.

메모리가 문제가되는 경우 검색 및 기타 처리 단계를 통합하거나 두 단계간에 소스를 다시 열 수 있는지 고려하십시오. 물론 주어진 파일을 자유롭게 탐색 할 수있는 능력을 가지고 있지만 문자 나 문자열을 제공하지는 않는 FileChannel을 이용하는 방법이 있습니다.

당신은 아마 getReadCharacters() 활용이 클래스의 reopenAt(BigInteger) (제대로 파일에 따라 BufferedReader의 연기에 대한 대체 드롭 테스트되지 않음) 할 수

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); 
     } 
    } 
} 
+1

답변 해 주셔서 감사합니다. readlimit를 파일보다 크게 변경했습니다. 그러나 독자가 eof에 도달하면 리셋이 실패한다는 가설은 여전히 ​​정확합니다. 나는 이것이 어디에서나 문서화되어 있다고 생각하지 않지만, 당신의 markablereader는이 문제를 해결하는데 크게 도움이되었다. 고마워요 – Evilsanta

+0

@Evilsanta, 오신 것을 환영합니다.또한 그 대답을 받아 들일 수 있습니다. – TheConstructor

+0

BufferedReader가 더 이상 문자를 건너 뛰기를 거부하면 중단됩니다 (BigInteger) -function. – TheConstructor

0

크지 만 파일과 같은 대형하지

이것이 문제입니다. 크기를 크게하거나 크게하십시오. 퀀텀보다 뒤쪽으로 다시 설정할 수는 없습니다.

하지만 파일을 전혀 다시 읽을 필요가 없습니다. 이는 디자인이 좋지 않고 프로그래밍이 잘 못됩니다. 컴파일러는 소스 파일을 한 번만 읽음으로써 프로그램을 컴파일 할 수 있으며 컴파일은 수행해야하는 프로세스보다 훨씬 복잡합니다.

+0

확실하게 달성 할 수 있지만, 이는 모든 것을 메모리에 저장하거나 복잡한 처리 방법을 섞어 야한다는 것을 의미 할 수 있습니다. 분명히 두 가지 방법 모두 프로그래머의 전반적인 성능을 향상시킬 수 있습니다. – TheConstructor

+0

마지막 행이 첫 번째 행에 의존하는 경우 모든 것을 메모리에 저장해야 함을 의미합니다. 이것은 가장 드문 경우입니다. – EJP

+0

일반적으로 파일을 메모리에 저장하지 않고 사전 처리 된 토큰을 유지할 수 있습니다. 예 : XML 구문 분석 마지막 라인을 읽을 때 어느 태그가 먼저 열렸는지를 알아야합니다. – TheConstructor

관련 문제