【Java8源码分析】IO包-Reader、BufferedReader和Scanner总结

时间:2022-09-24 21:36:12

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72886231


Java的IO类型可分为两大类:

  • 面向字符流:Reader & Writer
  • 面向字节流:InputStream & OutputStream

本文主要从源码方面分析一下常用的面向字符流的Reader。

  • Reader:所有字符流的抽象父类,定义了常用输入函数
  • BufferedReader:带有缓冲区的高效输入流

本文还会介绍一个常用的类:Scanner


1 Reader源码解析

public abstract class Reader implements Readable, Closeable {

// 读一个字符
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}

public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}

// 最主要的函数,子类需重写,读取一定长度的字符串
abstract public int read(char cbuf[], int off, int len) throws IOException;
}

Reader是所有xxxReader类的父类,子类需要重写read(char[], int, int)方法,大部分子类也会重写部分方法以提高效率。


2 BufferedReader源码解析

2.1 read()函数

public class BufferedReader extends Reader {

// 内部保存的Reader变量
private Reader in;

// 缓冲区
private char cb[];

// 默认缓冲区大小
private static int defaultCharBufferSize = 8192;

// 填充缓冲区的函数
private void fill() throws IOException {
int dst;

// 省略了部分定位的代码

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++];
}
}
}
}

BufferedReader的read()函数会读取一个字符,并且会跳过换行符。

read()都是从缓冲区中获取字符,缓冲区的默认大小为8KB,如果缓冲区读完了,则会调用fill()函数对缓冲区进行填充,然后再继续读。fill()则调用了read(char[], int, int)对缓冲区进行一次性填充。

2.2 read(char[], int, int)函数

public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {

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;
}
}

private int read1(char[] cbuf, int off, int len) throws IOException {
if (nextChar >= nChars) {
if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
// 调用输入流的read填充缓冲区
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;
}

read(char[], int, int)函数会尝试读取尽可能多的字符到数组中,直到满足以下条件之一停止:

  • 读取到指定长度的字符
  • 输入流返回-1表示结束
  • 输入流的ready函数返回false,说明请求新的字符将会被阻塞,直接返回

2.3 readLine函数

readLine()函数是返回从当前的缓冲位置,到换行符之间的字符串,即从缓冲区当前位置开始的一行。


3 Scanner源码解析

public final class Scanner implements Iterator<String>, Closeable {

// 缓冲区
private CharBuffer buf;

// 缓冲区大小1KB
private static final int BUFFER_SIZE = 1024;

public String nextLine() {
// 如果缓冲中的下一次匹配是行匹配,则直接返回
if (hasNextPattern == linePattern())
return getCachedResult();
clearCaches();

// 否则通过正则表达式匹配返回一行字符串
String result = findWithinHorizon(linePattern, 0);
if (result == null)
throw new NoSuchElementException("No line found");
MatchResult mr = this.match();
String lineSep = mr.group(1);
if (lineSep != null)
result = result.substring(0, result.length() - lineSep.length());
if (result == null)
throw new NoSuchElementException();
else
return result;
}
}

Sanner中的读取函数,不管是nextInt(),还是nextLine()都是采用正则匹配的方式读取的。

nextLine()函数是返回从当前的缓冲位置,到换行符之间的字符串,即从缓冲区当前位置开始的一行。


4 总结

(1)BufferedReader

BufferedReader的构造函数需要传入一个Reader,对于InputStream或者File,可以通过InputStreamReader和FileReader进行包装后传入,代码如下:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader br = new BufferedReader(new FileReader("foo.in"));

(2)Reader和Scanner总结

  • Java.util.Scanner类是一个简单的文本扫描类,它可以解析基本数据类型和字符串。它本质上是使用正则表达式去读取不同的数据类型
  • Java.io.BufferedReader类为了能够高效的读取字符序列,从字符输入流和字符缓冲区读取文本

(3)readLine和nextLine函数

不管是Reader还是Scanner,如果之前调用read()或者nextXxx()读取过字符,再调用readLine()或者nextLine(0函数,都是读取本行剩余部分。

(4)Reader和Scanner比较

  • BufferedReader是支持同步的,而Scanner不支持。BufferedReader的read函数都加了synchronized关键字
  • BufferedReader的缓冲区大小为8KB,Scanner的缓冲区大小为1KB
  • BufferedReader相对于Scanner来说要快一点,因为Scanner对输入数据进行正则解析,而BufferedReader只是简单地读取字符序列

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72886231