Java-IO之BufferedReader(字符缓冲输入流)

时间:2022-06-16 04:01:28
BufferedReader是缓冲字符输入流,继承于Reader,BufferedReader的作用是为其他字符输入流添加一些缓冲功能。
BufferedReader主要的函数列表:

BufferedReader(Reader in)
BufferedReader(Reader in, int size)

void close()
void mark(int markLimit)
boolean markSupported()
int read()
int read(char[] buffer, int offset, int length)
String readLine()
boolean ready()
void reset()
long skip(long charCount)
示例代码:

public class BufferedReaderTest {

private static final int LEN = 5;

public static void main(String[] args) {
testBufferedReader() ;
}

/**
* BufferedReader的API测试函数
*/
private static void testBufferedReader() {

// 创建BufferedReader字符流,内容是ArrayLetters数组
try {
File file = new File("out.txt");
BufferedReader in =new BufferedReader(new FileReader(file));

// 从字符流中读取5个字符。“abcde”
for (int i=0; i<LEN; i++) {
// 若能继续读取下一个字符,则读取下一个字符
if (in.ready()) {
// 读取“字符流的下一个字符”
int tmp = in.read();
System.out.printf("%d : %c\n", i, tmp);
}
}

// 若“该字符流”不支持标记功能,则直接退出
if (!in.markSupported()) {
System.out.println("make not supported!");
return ;
}

// 标记“当前索引位置”,即标记第6个位置的元素--“f”
// 1024对应marklimit
in.mark(1024);

// 跳过22个字符。
in.skip(22);

// 读取5个字符
char[] buf = new char[LEN];
in.read(buf, 0, LEN);
System.out.printf("buf=%s\n", String.valueOf(buf));
// 读取该行剩余的数据
System.out.printf("readLine=%s\n", in.readLine());

// 重置“输入流的索引”为mark()所标记的位置,即重置到“f”处。
in.reset();
// 从“重置后的字符流”中读取5个字符到buf中。即读取“fghij”
in.read(buf, 0, LEN);
System.out.printf("buf=%s\n", String.valueOf(buf));

in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果: 0 : a 1 : b 2 : c 3 : d 4 : e buf= 基于JDK8的BufferedReader源码分析:

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;

//构造函数,字符输入流,参数是Reader和输入缓冲大小
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;
}
//默认大小,参数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 */
char ncb[] = new char[readAheadLimit];
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;
}
}
//读单个字符
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.
*/
//读到数组cbuf中
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;
}
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;
}
}
//读一行
String readLine(boolean ignoreLF) throws IOException {
StringBuffer 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 StringBuffer(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'), or a carriage return
* followed immediately by a linefeed.
*
* @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
*
* @exception IOException If an I/O error occurs
*
* @see java.nio.file.Files#readAllLines
*/
//读一行,直到'\n','\r',或者回车
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;
}
}
//是否可读
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>() {
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);
}
}