608 lines
21 KiB
Java
608 lines
21 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2014 The Android Open Source Project
|
||
|
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
|
||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||
|
*
|
||
|
* This code is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU General Public License version 2 only, as
|
||
|
* published by the Free Software Foundation. Oracle designates this
|
||
|
* particular file as subject to the "Classpath" exception as provided
|
||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||
|
*
|
||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||
|
* accompanied this code).
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License version
|
||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*
|
||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||
|
* or visit www.oracle.com if you need additional information or have any
|
||
|
* questions.
|
||
|
*/
|
||
|
|
||
|
package java.io;
|
||
|
|
||
|
|
||
|
import java.util.Iterator;
|
||
|
import java.util.NoSuchElementException;
|
||
|
import java.util.Spliterator;
|
||
|
import java.util.Spliterators;
|
||
|
import java.util.stream.Stream;
|
||
|
import java.util.stream.StreamSupport;
|
||
|
|
||
|
/**
|
||
|
* Reads text from a character-input stream, buffering characters so as to
|
||
|
* provide for the efficient reading of characters, arrays, and lines.
|
||
|
*
|
||
|
* <p> The buffer size may be specified, or the default size may be used. The
|
||
|
* default is large enough for most purposes.
|
||
|
*
|
||
|
* <p> In general, each read request made of a Reader causes a corresponding
|
||
|
* read request to be made of the underlying character or byte stream. It is
|
||
|
* therefore advisable to wrap a BufferedReader around any Reader whose read()
|
||
|
* operations may be costly, such as FileReaders and InputStreamReaders. For
|
||
|
* example,
|
||
|
*
|
||
|
* <pre>
|
||
|
* BufferedReader in
|
||
|
* = new BufferedReader(new FileReader("foo.in"));
|
||
|
* </pre>
|
||
|
*
|
||
|
* will buffer the input from the specified file. Without buffering, each
|
||
|
* invocation of read() or readLine() could cause bytes to be read from the
|
||
|
* file, converted into characters, and then returned, which can be very
|
||
|
* inefficient.
|
||
|
*
|
||
|
* <p> Programs that use DataInputStreams for textual input can be localized by
|
||
|
* replacing each DataInputStream with an appropriate BufferedReader.
|
||
|
*
|
||
|
* @see FileReader
|
||
|
* @see InputStreamReader
|
||
|
* @see java.nio.file.Files#newBufferedReader
|
||
|
*
|
||
|
* @author Mark Reinhold
|
||
|
* @since 1.1
|
||
|
*/
|
||
|
|
||
|
public class BufferedReader extends Reader {
|
||
|
|
||
|
private Reader in;
|
||
|
|
||
|
private char cb[];
|
||
|
private int nChars, nextChar;
|
||
|
|
||
|
private static final int INVALIDATED = -2;
|
||
|
private static final int UNMARKED = -1;
|
||
|
private int markedChar = UNMARKED;
|
||
|
private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
|
||
|
|
||
|
/** If the next character is a line feed, skip it */
|
||
|
private boolean skipLF = false;
|
||
|
|
||
|
/** The skipLF flag when the mark was set */
|
||
|
private boolean markedSkipLF = false;
|
||
|
|
||
|
private static int defaultCharBufferSize = 8192;
|
||
|
private static int defaultExpectedLineLength = 80;
|
||
|
|
||
|
/**
|
||
|
* Creates a buffering character-input stream that uses an input buffer of
|
||
|
* the specified size.
|
||
|
*
|
||
|
* @param in A Reader
|
||
|
* @param sz Input-buffer size
|
||
|
*
|
||
|
* @exception IllegalArgumentException If {@code sz <= 0}
|
||
|
*/
|
||
|
public BufferedReader(Reader in, int sz) {
|
||
|
super(in);
|
||
|
if (sz <= 0)
|
||
|
throw new IllegalArgumentException("Buffer size <= 0");
|
||
|
this.in = in;
|
||
|
cb = new char[sz];
|
||
|
nextChar = nChars = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a buffering character-input stream that uses a default-sized
|
||
|
* input buffer.
|
||
|
*
|
||
|
* @param in A Reader
|
||
|
*/
|
||
|
public BufferedReader(Reader in) {
|
||
|
this(in, defaultCharBufferSize);
|
||
|
}
|
||
|
|
||
|
/** Checks to make sure that the stream has not been closed */
|
||
|
private void ensureOpen() throws IOException {
|
||
|
if (in == null)
|
||
|
throw new IOException("Stream closed");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fills the input buffer, taking the mark into account if it is valid.
|
||
|
*/
|
||
|
private void fill() throws IOException {
|
||
|
int dst;
|
||
|
if (markedChar <= UNMARKED) {
|
||
|
/* No mark */
|
||
|
dst = 0;
|
||
|
} else {
|
||
|
/* Marked */
|
||
|
int delta = nextChar - markedChar;
|
||
|
if (delta >= readAheadLimit) {
|
||
|
/* Gone past read-ahead limit: Invalidate mark */
|
||
|
markedChar = INVALIDATED;
|
||
|
readAheadLimit = 0;
|
||
|
dst = 0;
|
||
|
} else {
|
||
|
if (readAheadLimit <= cb.length) {
|
||
|
/* Shuffle in the current buffer */
|
||
|
System.arraycopy(cb, markedChar, cb, 0, delta);
|
||
|
markedChar = 0;
|
||
|
dst = delta;
|
||
|
} else {
|
||
|
/* Reallocate buffer to accommodate read-ahead limit */
|
||
|
//
|
||
|
// Android-changed: Use the same strategy as BufferedInputStream,
|
||
|
// i.e, double the size of the buffer on each fill. Do not directly
|
||
|
// size the buffer to the readAheadLimit.
|
||
|
//
|
||
|
// char ncb[] = new char[readAheadLimit];
|
||
|
int nlength = cb.length * 2;
|
||
|
if (nlength > readAheadLimit) {
|
||
|
nlength = readAheadLimit;
|
||
|
}
|
||
|
char ncb[] = new char[nlength];
|
||
|
System.arraycopy(cb, markedChar, ncb, 0, delta);
|
||
|
cb = ncb;
|
||
|
markedChar = 0;
|
||
|
dst = delta;
|
||
|
}
|
||
|
nextChar = nChars = delta;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int n;
|
||
|
do {
|
||
|
n = in.read(cb, dst, cb.length - dst);
|
||
|
} while (n == 0);
|
||
|
if (n > 0) {
|
||
|
nChars = dst + n;
|
||
|
nextChar = dst;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads a single character.
|
||
|
*
|
||
|
* @return The character read, as an integer in the range
|
||
|
* 0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
|
||
|
* end of the stream has been reached
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*/
|
||
|
public int read() throws IOException {
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
for (;;) {
|
||
|
if (nextChar >= nChars) {
|
||
|
fill();
|
||
|
if (nextChar >= nChars)
|
||
|
return -1;
|
||
|
}
|
||
|
if (skipLF) {
|
||
|
skipLF = false;
|
||
|
if (cb[nextChar] == '\n') {
|
||
|
nextChar++;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return cb[nextChar++];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads characters into a portion of an array, reading from the underlying
|
||
|
* stream if necessary.
|
||
|
*/
|
||
|
private int read1(char[] cbuf, int off, int len) throws IOException {
|
||
|
if (nextChar >= nChars) {
|
||
|
/* If the requested length is at least as large as the buffer, and
|
||
|
if there is no mark/reset activity, and if line feeds are not
|
||
|
being skipped, do not bother to copy the characters into the
|
||
|
local buffer. In this way buffered streams will cascade
|
||
|
harmlessly. */
|
||
|
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
|
||
|
return in.read(cbuf, off, len);
|
||
|
}
|
||
|
fill();
|
||
|
}
|
||
|
if (nextChar >= nChars) return -1;
|
||
|
if (skipLF) {
|
||
|
skipLF = false;
|
||
|
if (cb[nextChar] == '\n') {
|
||
|
nextChar++;
|
||
|
if (nextChar >= nChars)
|
||
|
fill();
|
||
|
if (nextChar >= nChars)
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
int n = Math.min(len, nChars - nextChar);
|
||
|
System.arraycopy(cb, nextChar, cbuf, off, n);
|
||
|
nextChar += n;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads characters into a portion of an array.
|
||
|
*
|
||
|
* <p> This method implements the general contract of the corresponding
|
||
|
* <code>{@link Reader#read(char[], int, int) read}</code> method of the
|
||
|
* <code>{@link Reader}</code> class. As an additional convenience, it
|
||
|
* attempts to read as many characters as possible by repeatedly invoking
|
||
|
* the <code>read</code> method of the underlying stream. This iterated
|
||
|
* <code>read</code> continues until one of the following conditions becomes
|
||
|
* true: <ul>
|
||
|
*
|
||
|
* <li> The specified number of characters have been read,
|
||
|
*
|
||
|
* <li> The <code>read</code> method of the underlying stream returns
|
||
|
* <code>-1</code>, indicating end-of-file, or
|
||
|
*
|
||
|
* <li> The <code>ready</code> method of the underlying stream
|
||
|
* returns <code>false</code>, indicating that further input requests
|
||
|
* would block.
|
||
|
*
|
||
|
* </ul> If the first <code>read</code> on the underlying stream returns
|
||
|
* <code>-1</code> to indicate end-of-file then this method returns
|
||
|
* <code>-1</code>. Otherwise this method returns the number of characters
|
||
|
* actually read.
|
||
|
*
|
||
|
* <p> Subclasses of this class are encouraged, but not required, to
|
||
|
* attempt to read as many characters as possible in the same fashion.
|
||
|
*
|
||
|
* <p> Ordinarily this method takes characters from this stream's character
|
||
|
* buffer, filling it from the underlying stream as necessary. If,
|
||
|
* however, the buffer is empty, the mark is not valid, and the requested
|
||
|
* length is at least as large as the buffer, then this method will read
|
||
|
* characters directly from the underlying stream into the given array.
|
||
|
* Thus redundant <code>BufferedReader</code>s will not copy data
|
||
|
* unnecessarily.
|
||
|
*
|
||
|
* @param cbuf Destination buffer
|
||
|
* @param off Offset at which to start storing characters
|
||
|
* @param len Maximum number of characters to read
|
||
|
*
|
||
|
* @return The number of characters read, or -1 if the end of the
|
||
|
* stream has been reached
|
||
|
*
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
* @exception IndexOutOfBoundsException {@inheritDoc}
|
||
|
*/
|
||
|
public int read(char cbuf[], int off, int len) throws IOException {
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
|
||
|
((off + len) > cbuf.length) || ((off + len) < 0)) {
|
||
|
throw new IndexOutOfBoundsException();
|
||
|
} else if (len == 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int n = read1(cbuf, off, len);
|
||
|
if (n <= 0) return n;
|
||
|
while ((n < len) && in.ready()) {
|
||
|
int n1 = read1(cbuf, off + n, len - n);
|
||
|
if (n1 <= 0) break;
|
||
|
n += n1;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads a line of text. A line is considered to be terminated by any one
|
||
|
* of a line feed ('\n'), a carriage return ('\r'), a carriage return
|
||
|
* followed immediately by a line feed, or by reaching the end-of-file
|
||
|
* (EOF).
|
||
|
*
|
||
|
* @param ignoreLF If true, the next '\n' will be skipped
|
||
|
*
|
||
|
* @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 without reading any characters
|
||
|
*
|
||
|
* @see java.io.LineNumberReader#readLine()
|
||
|
*
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*/
|
||
|
String readLine(boolean ignoreLF) throws IOException {
|
||
|
StringBuilder s = null;
|
||
|
int startChar;
|
||
|
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
boolean omitLF = ignoreLF || skipLF;
|
||
|
|
||
|
bufferLoop:
|
||
|
for (;;) {
|
||
|
|
||
|
if (nextChar >= nChars)
|
||
|
fill();
|
||
|
if (nextChar >= nChars) { /* EOF */
|
||
|
if (s != null && s.length() > 0)
|
||
|
return s.toString();
|
||
|
else
|
||
|
return null;
|
||
|
}
|
||
|
boolean eol = false;
|
||
|
char c = 0;
|
||
|
int i;
|
||
|
|
||
|
/* Skip a leftover '\n', if necessary */
|
||
|
if (omitLF && (cb[nextChar] == '\n'))
|
||
|
nextChar++;
|
||
|
skipLF = false;
|
||
|
omitLF = false;
|
||
|
|
||
|
charLoop:
|
||
|
for (i = nextChar; i < nChars; i++) {
|
||
|
c = cb[i];
|
||
|
if ((c == '\n') || (c == '\r')) {
|
||
|
eol = true;
|
||
|
break charLoop;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
startChar = nextChar;
|
||
|
nextChar = i;
|
||
|
|
||
|
if (eol) {
|
||
|
String str;
|
||
|
if (s == null) {
|
||
|
str = new String(cb, startChar, i - startChar);
|
||
|
} else {
|
||
|
s.append(cb, startChar, i - startChar);
|
||
|
str = s.toString();
|
||
|
}
|
||
|
nextChar++;
|
||
|
if (c == '\r') {
|
||
|
skipLF = true;
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
if (s == null)
|
||
|
s = new StringBuilder(defaultExpectedLineLength);
|
||
|
s.append(cb, startChar, i - startChar);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads a line of text. A line is considered to be terminated by any one
|
||
|
* of a line feed ('\n'), a carriage return ('\r'), a carriage return
|
||
|
* followed immediately by a line feed, or by reaching the end-of-file
|
||
|
* (EOF).
|
||
|
*
|
||
|
* @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 without reading any characters
|
||
|
*
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*
|
||
|
* @see java.nio.file.Files#readAllLines
|
||
|
*/
|
||
|
public String readLine() throws IOException {
|
||
|
return readLine(false);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Skips characters.
|
||
|
*
|
||
|
* @param n The number of characters to skip
|
||
|
*
|
||
|
* @return The number of characters actually skipped
|
||
|
*
|
||
|
* @exception IllegalArgumentException If <code>n</code> is negative.
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*/
|
||
|
public long skip(long n) throws IOException {
|
||
|
if (n < 0L) {
|
||
|
throw new IllegalArgumentException("skip value is negative");
|
||
|
}
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
long r = n;
|
||
|
while (r > 0) {
|
||
|
if (nextChar >= nChars)
|
||
|
fill();
|
||
|
if (nextChar >= nChars) /* EOF */
|
||
|
break;
|
||
|
if (skipLF) {
|
||
|
skipLF = false;
|
||
|
if (cb[nextChar] == '\n') {
|
||
|
nextChar++;
|
||
|
}
|
||
|
}
|
||
|
long d = nChars - nextChar;
|
||
|
if (r <= d) {
|
||
|
nextChar += r;
|
||
|
r = 0;
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
r -= d;
|
||
|
nextChar = nChars;
|
||
|
}
|
||
|
}
|
||
|
return n - r;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tells whether this stream is ready to be read. A buffered character
|
||
|
* stream is ready if the buffer is not empty, or if the underlying
|
||
|
* character stream is ready.
|
||
|
*
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*/
|
||
|
public boolean ready() throws IOException {
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
|
||
|
/*
|
||
|
* If newline needs to be skipped and the next char to be read
|
||
|
* is a newline character, then just skip it right away.
|
||
|
*/
|
||
|
if (skipLF) {
|
||
|
/* Note that in.ready() will return true if and only if the next
|
||
|
* read on the stream will not block.
|
||
|
*/
|
||
|
if (nextChar >= nChars && in.ready()) {
|
||
|
fill();
|
||
|
}
|
||
|
if (nextChar < nChars) {
|
||
|
if (cb[nextChar] == '\n')
|
||
|
nextChar++;
|
||
|
skipLF = false;
|
||
|
}
|
||
|
}
|
||
|
return (nextChar < nChars) || in.ready();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tells whether this stream supports the mark() operation, which it does.
|
||
|
*/
|
||
|
public boolean markSupported() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Marks the present position in the stream. Subsequent calls to reset()
|
||
|
* will attempt to reposition the stream to this point.
|
||
|
*
|
||
|
* @param readAheadLimit Limit on the number of characters that may be
|
||
|
* read while still preserving the mark. An attempt
|
||
|
* to reset the stream after reading characters
|
||
|
* up to this limit or beyond may fail.
|
||
|
* 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.
|
||
|
*
|
||
|
* @exception IllegalArgumentException If {@code readAheadLimit < 0}
|
||
|
* @exception IOException If an I/O error occurs
|
||
|
*/
|
||
|
public void mark(int readAheadLimit) throws IOException {
|
||
|
if (readAheadLimit < 0) {
|
||
|
throw new IllegalArgumentException("Read-ahead limit < 0");
|
||
|
}
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
this.readAheadLimit = readAheadLimit;
|
||
|
markedChar = nextChar;
|
||
|
markedSkipLF = skipLF;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Resets the stream to the most recent mark.
|
||
|
*
|
||
|
* @exception IOException If the stream has never been marked,
|
||
|
* or if the mark has been invalidated
|
||
|
*/
|
||
|
public void reset() throws IOException {
|
||
|
synchronized (lock) {
|
||
|
ensureOpen();
|
||
|
if (markedChar < 0)
|
||
|
throw new IOException((markedChar == INVALIDATED)
|
||
|
? "Mark invalid"
|
||
|
: "Stream not marked");
|
||
|
nextChar = markedChar;
|
||
|
skipLF = markedSkipLF;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void close() throws IOException {
|
||
|
synchronized (lock) {
|
||
|
if (in == null)
|
||
|
return;
|
||
|
try {
|
||
|
in.close();
|
||
|
} finally {
|
||
|
in = null;
|
||
|
cb = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a {@code Stream}, the elements of which are lines read from
|
||
|
* this {@code BufferedReader}. The {@link Stream} is lazily populated,
|
||
|
* i.e., read only occurs during the
|
||
|
* <a href="../util/stream/package-summary.html#StreamOps">terminal
|
||
|
* stream operation</a>.
|
||
|
*
|
||
|
* <p> The reader must not be operated on during the execution of the
|
||
|
* terminal stream operation. Otherwise, the result of the terminal stream
|
||
|
* operation is undefined.
|
||
|
*
|
||
|
* <p> After execution of the terminal stream operation there are no
|
||
|
* guarantees that the reader will be at a specific position from which to
|
||
|
* read the next character or line.
|
||
|
*
|
||
|
* <p> If an {@link IOException} is thrown when accessing the underlying
|
||
|
* {@code BufferedReader}, it is wrapped in an {@link
|
||
|
* UncheckedIOException} which will be thrown from the {@code Stream}
|
||
|
* method that caused the read to take place. This method will return a
|
||
|
* Stream if invoked on a BufferedReader that is closed. Any operation on
|
||
|
* that stream that requires reading from the BufferedReader after it is
|
||
|
* closed, will cause an UncheckedIOException to be thrown.
|
||
|
*
|
||
|
* @return a {@code Stream<String>} providing the lines of text
|
||
|
* described by this {@code BufferedReader}
|
||
|
*
|
||
|
* @since 1.8
|
||
|
*/
|
||
|
public Stream<String> lines() {
|
||
|
Iterator<String> iter = new Iterator<>() {
|
||
|
String nextLine = null;
|
||
|
|
||
|
@Override
|
||
|
public boolean hasNext() {
|
||
|
if (nextLine != null) {
|
||
|
return true;
|
||
|
} else {
|
||
|
try {
|
||
|
nextLine = readLine();
|
||
|
return (nextLine != null);
|
||
|
} catch (IOException e) {
|
||
|
throw new UncheckedIOException(e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String next() {
|
||
|
if (nextLine != null || hasNext()) {
|
||
|
String line = nextLine;
|
||
|
nextLine = null;
|
||
|
return line;
|
||
|
} else {
|
||
|
throw new NoSuchElementException();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
|
||
|
iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
|
||
|
}
|
||
|
}
|