转载请注明出处: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