Path: blob/master/src/java.base/share/classes/java/io/BufferedReader.java
41152 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.io;262728import java.util.Iterator;29import java.util.NoSuchElementException;30import java.util.Objects;31import java.util.Spliterator;32import java.util.Spliterators;33import java.util.stream.Stream;34import java.util.stream.StreamSupport;3536/**37* Reads text from a character-input stream, buffering characters so as to38* provide for the efficient reading of characters, arrays, and lines.39*40* <p> The buffer size may be specified, or the default size may be used. The41* default is large enough for most purposes.42*43* <p> In general, each read request made of a Reader causes a corresponding44* read request to be made of the underlying character or byte stream. It is45* therefore advisable to wrap a BufferedReader around any Reader whose read()46* operations may be costly, such as FileReaders and InputStreamReaders. For47* example,48*49* <pre>50* BufferedReader in51* = new BufferedReader(new FileReader("foo.in"));52* </pre>53*54* will buffer the input from the specified file. Without buffering, each55* invocation of read() or readLine() could cause bytes to be read from the56* file, converted into characters, and then returned, which can be very57* inefficient.58*59* <p> Programs that use DataInputStreams for textual input can be localized by60* replacing each DataInputStream with an appropriate BufferedReader.61*62* @see FileReader63* @see InputStreamReader64* @see java.nio.file.Files#newBufferedReader65*66* @author Mark Reinhold67* @since 1.168*/6970public class BufferedReader extends Reader {7172private Reader in;7374private char[] cb;75private int nChars, nextChar;7677private static final int INVALIDATED = -2;78private static final int UNMARKED = -1;79private int markedChar = UNMARKED;80private int readAheadLimit = 0; /* Valid only when markedChar > 0 */8182/** If the next character is a line feed, skip it */83private boolean skipLF = false;8485/** The skipLF flag when the mark was set */86private boolean markedSkipLF = false;8788private static int defaultCharBufferSize = 8192;89private static int defaultExpectedLineLength = 80;9091/**92* Creates a buffering character-input stream that uses an input buffer of93* the specified size.94*95* @param in A Reader96* @param sz Input-buffer size97*98* @throws IllegalArgumentException If {@code sz <= 0}99*/100public BufferedReader(Reader in, int sz) {101super(in);102if (sz <= 0)103throw new IllegalArgumentException("Buffer size <= 0");104this.in = in;105cb = new char[sz];106nextChar = nChars = 0;107}108109/**110* Creates a buffering character-input stream that uses a default-sized111* input buffer.112*113* @param in A Reader114*/115public BufferedReader(Reader in) {116this(in, defaultCharBufferSize);117}118119/** Checks to make sure that the stream has not been closed */120private void ensureOpen() throws IOException {121if (in == null)122throw new IOException("Stream closed");123}124125/**126* Fills the input buffer, taking the mark into account if it is valid.127*/128private void fill() throws IOException {129int dst;130if (markedChar <= UNMARKED) {131/* No mark */132dst = 0;133} else {134/* Marked */135int delta = nextChar - markedChar;136if (delta >= readAheadLimit) {137/* Gone past read-ahead limit: Invalidate mark */138markedChar = INVALIDATED;139readAheadLimit = 0;140dst = 0;141} else {142if (readAheadLimit <= cb.length) {143/* Shuffle in the current buffer */144System.arraycopy(cb, markedChar, cb, 0, delta);145markedChar = 0;146dst = delta;147} else {148/* Reallocate buffer to accommodate read-ahead limit */149char[] ncb = new char[readAheadLimit];150System.arraycopy(cb, markedChar, ncb, 0, delta);151cb = ncb;152markedChar = 0;153dst = delta;154}155nextChar = nChars = delta;156}157}158159int n;160do {161n = in.read(cb, dst, cb.length - dst);162} while (n == 0);163if (n > 0) {164nChars = dst + n;165nextChar = dst;166}167}168169/**170* Reads a single character.171*172* @return The character read, as an integer in the range173* 0 to 65535 ({@code 0x00-0xffff}), or -1 if the174* end of the stream has been reached175* @throws IOException If an I/O error occurs176*/177public int read() throws IOException {178synchronized (lock) {179ensureOpen();180for (;;) {181if (nextChar >= nChars) {182fill();183if (nextChar >= nChars)184return -1;185}186if (skipLF) {187skipLF = false;188if (cb[nextChar] == '\n') {189nextChar++;190continue;191}192}193return cb[nextChar++];194}195}196}197198/**199* Reads characters into a portion of an array, reading from the underlying200* stream if necessary.201*/202private int read1(char[] cbuf, int off, int len) throws IOException {203if (nextChar >= nChars) {204/* If the requested length is at least as large as the buffer, and205if there is no mark/reset activity, and if line feeds are not206being skipped, do not bother to copy the characters into the207local buffer. In this way buffered streams will cascade208harmlessly. */209if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {210return in.read(cbuf, off, len);211}212fill();213}214if (nextChar >= nChars) return -1;215if (skipLF) {216skipLF = false;217if (cb[nextChar] == '\n') {218nextChar++;219if (nextChar >= nChars)220fill();221if (nextChar >= nChars)222return -1;223}224}225int n = Math.min(len, nChars - nextChar);226System.arraycopy(cb, nextChar, cbuf, off, n);227nextChar += n;228return n;229}230231/**232* Reads characters into a portion of an array.233*234* <p> This method implements the general contract of the corresponding235* {@link Reader#read(char[], int, int) read} method of the236* {@link Reader} class. As an additional convenience, it237* attempts to read as many characters as possible by repeatedly invoking238* the {@code read} method of the underlying stream. This iterated239* {@code read} continues until one of the following conditions becomes240* true:241* <ul>242*243* <li> The specified number of characters have been read,244*245* <li> The {@code read} method of the underlying stream returns246* {@code -1}, indicating end-of-file, or247*248* <li> The {@code ready} method of the underlying stream249* returns {@code false}, indicating that further input requests250* would block.251*252* </ul>253* If the first {@code read} on the underlying stream returns254* {@code -1} to indicate end-of-file then this method returns255* {@code -1}. Otherwise this method returns the number of characters256* actually read.257*258* <p> Subclasses of this class are encouraged, but not required, to259* attempt to read as many characters as possible in the same fashion.260*261* <p> Ordinarily this method takes characters from this stream's character262* buffer, filling it from the underlying stream as necessary. If,263* however, the buffer is empty, the mark is not valid, and the requested264* length is at least as large as the buffer, then this method will read265* characters directly from the underlying stream into the given array.266* Thus redundant {@code BufferedReader}s will not copy data267* unnecessarily.268*269* @param cbuf {@inheritDoc}270* @param off {@inheritDoc}271* @param len {@inheritDoc}272*273* @return {@inheritDoc}274*275* @throws IndexOutOfBoundsException {@inheritDoc}276* @throws IOException {@inheritDoc}277*/278public int read(char[] cbuf, int off, int len) throws IOException {279synchronized (lock) {280ensureOpen();281Objects.checkFromIndexSize(off, len, cbuf.length);282if (len == 0) {283return 0;284}285286int n = read1(cbuf, off, len);287if (n <= 0) return n;288while ((n < len) && in.ready()) {289int n1 = read1(cbuf, off + n, len - n);290if (n1 <= 0) break;291n += n1;292}293return n;294}295}296297/**298* Reads a line of text. A line is considered to be terminated by any one299* of a line feed ('\n'), a carriage return ('\r'), a carriage return300* followed immediately by a line feed, or by reaching the end-of-file301* (EOF).302*303* @param ignoreLF If true, the next '\n' will be skipped304* @param term Output: Whether a line terminator was encountered305* while reading the line; may be {@code null}.306*307* @return A String containing the contents of the line, not including308* any line-termination characters, or null if the end of the309* stream has been reached without reading any characters310*311* @see java.io.LineNumberReader#readLine()312*313* @throws IOException If an I/O error occurs314*/315String readLine(boolean ignoreLF, boolean[] term) throws IOException {316StringBuilder s = null;317int startChar;318319synchronized (lock) {320ensureOpen();321boolean omitLF = ignoreLF || skipLF;322if (term != null) term[0] = false;323324bufferLoop:325for (;;) {326327if (nextChar >= nChars)328fill();329if (nextChar >= nChars) { /* EOF */330if (s != null && s.length() > 0)331return s.toString();332else333return null;334}335boolean eol = false;336char c = 0;337int i;338339/* Skip a leftover '\n', if necessary */340if (omitLF && (cb[nextChar] == '\n'))341nextChar++;342skipLF = false;343omitLF = false;344345charLoop:346for (i = nextChar; i < nChars; i++) {347c = cb[i];348if ((c == '\n') || (c == '\r')) {349if (term != null) term[0] = true;350eol = true;351break charLoop;352}353}354355startChar = nextChar;356nextChar = i;357358if (eol) {359String str;360if (s == null) {361str = new String(cb, startChar, i - startChar);362} else {363s.append(cb, startChar, i - startChar);364str = s.toString();365}366nextChar++;367if (c == '\r') {368skipLF = true;369}370return str;371}372373if (s == null)374s = new StringBuilder(defaultExpectedLineLength);375s.append(cb, startChar, i - startChar);376}377}378}379380/**381* Reads a line of text. A line is considered to be terminated by any one382* of a line feed ('\n'), a carriage return ('\r'), a carriage return383* followed immediately by a line feed, or by reaching the end-of-file384* (EOF).385*386* @return A String containing the contents of the line, not including387* any line-termination characters, or null if the end of the388* stream has been reached without reading any characters389*390* @throws IOException If an I/O error occurs391*392* @see java.nio.file.Files#readAllLines393*/394public String readLine() throws IOException {395return readLine(false, null);396}397398/**399* {@inheritDoc}400*/401public long skip(long n) throws IOException {402if (n < 0L) {403throw new IllegalArgumentException("skip value is negative");404}405synchronized (lock) {406ensureOpen();407long r = n;408while (r > 0) {409if (nextChar >= nChars)410fill();411if (nextChar >= nChars) /* EOF */412break;413if (skipLF) {414skipLF = false;415if (cb[nextChar] == '\n') {416nextChar++;417}418}419long d = nChars - nextChar;420if (r <= d) {421nextChar += r;422r = 0;423break;424}425else {426r -= d;427nextChar = nChars;428}429}430return n - r;431}432}433434/**435* Tells whether this stream is ready to be read. A buffered character436* stream is ready if the buffer is not empty, or if the underlying437* character stream is ready.438*439* @throws IOException If an I/O error occurs440*/441public boolean ready() throws IOException {442synchronized (lock) {443ensureOpen();444445/*446* If newline needs to be skipped and the next char to be read447* is a newline character, then just skip it right away.448*/449if (skipLF) {450/* Note that in.ready() will return true if and only if the next451* read on the stream will not block.452*/453if (nextChar >= nChars && in.ready()) {454fill();455}456if (nextChar < nChars) {457if (cb[nextChar] == '\n')458nextChar++;459skipLF = false;460}461}462return (nextChar < nChars) || in.ready();463}464}465466/**467* Tells whether this stream supports the mark() operation, which it does.468*/469public boolean markSupported() {470return true;471}472473/**474* Marks the present position in the stream. Subsequent calls to reset()475* will attempt to reposition the stream to this point.476*477* @param readAheadLimit Limit on the number of characters that may be478* read while still preserving the mark. An attempt479* to reset the stream after reading characters480* up to this limit or beyond may fail.481* A limit value larger than the size of the input482* buffer will cause a new buffer to be allocated483* whose size is no smaller than limit.484* Therefore large values should be used with care.485*486* @throws IllegalArgumentException If {@code readAheadLimit < 0}487* @throws IOException If an I/O error occurs488*/489public void mark(int readAheadLimit) throws IOException {490if (readAheadLimit < 0) {491throw new IllegalArgumentException("Read-ahead limit < 0");492}493synchronized (lock) {494ensureOpen();495this.readAheadLimit = readAheadLimit;496markedChar = nextChar;497markedSkipLF = skipLF;498}499}500501/**502* Resets the stream to the most recent mark.503*504* @throws IOException If the stream has never been marked,505* or if the mark has been invalidated506*/507public void reset() throws IOException {508synchronized (lock) {509ensureOpen();510if (markedChar < 0)511throw new IOException((markedChar == INVALIDATED)512? "Mark invalid"513: "Stream not marked");514nextChar = markedChar;515skipLF = markedSkipLF;516}517}518519public void close() throws IOException {520synchronized (lock) {521if (in == null)522return;523try {524in.close();525} finally {526in = null;527cb = null;528}529}530}531532/**533* Returns a {@code Stream}, the elements of which are lines read from534* this {@code BufferedReader}. The {@link Stream} is lazily populated,535* i.e., read only occurs during the536* <a href="../util/stream/package-summary.html#StreamOps">terminal537* stream operation</a>.538*539* <p> The reader must not be operated on during the execution of the540* terminal stream operation. Otherwise, the result of the terminal stream541* operation is undefined.542*543* <p> After execution of the terminal stream operation there are no544* guarantees that the reader will be at a specific position from which to545* read the next character or line.546*547* <p> If an {@link IOException} is thrown when accessing the underlying548* {@code BufferedReader}, it is wrapped in an {@link549* UncheckedIOException} which will be thrown from the {@code Stream}550* method that caused the read to take place. This method will return a551* Stream if invoked on a BufferedReader that is closed. Any operation on552* that stream that requires reading from the BufferedReader after it is553* closed, will cause an UncheckedIOException to be thrown.554*555* @return a {@code Stream<String>} providing the lines of text556* described by this {@code BufferedReader}557*558* @since 1.8559*/560public Stream<String> lines() {561Iterator<String> iter = new Iterator<>() {562String nextLine = null;563564@Override565public boolean hasNext() {566if (nextLine != null) {567return true;568} else {569try {570nextLine = readLine();571return (nextLine != null);572} catch (IOException e) {573throw new UncheckedIOException(e);574}575}576}577578@Override579public String next() {580if (nextLine != null || hasNext()) {581String line = nextLine;582nextLine = null;583return line;584} else {585throw new NoSuchElementException();586}587}588};589return StreamSupport.stream(Spliterators.spliteratorUnknownSize(590iter, Spliterator.ORDERED | Spliterator.NONNULL), false);591}592}593594595