나는 그것을 명확하게
매개 변수를 명시 어디가 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);
}
}
}
답변 해 주셔서 감사합니다. readlimit를 파일보다 크게 변경했습니다. 그러나 독자가 eof에 도달하면 리셋이 실패한다는 가설은 여전히 정확합니다. 나는 이것이 어디에서나 문서화되어 있다고 생각하지 않지만, 당신의 markablereader는이 문제를 해결하는데 크게 도움이되었다. 고마워요 – Evilsanta
@Evilsanta, 오신 것을 환영합니다.또한 그 대답을 받아 들일 수 있습니다. – TheConstructor
BufferedReader가 더 이상 문자를 건너 뛰기를 거부하면 중단됩니다 (BigInteger) -function. – TheConstructor