即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

【Java语言8源码解析】IO包-Reader、BufferedReader与Scanner深度总结

编程语言 linxdcn 104℃ 0评论
本文目录
[隐藏]

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


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

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

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

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

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


1.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.2 BufferedReader源码解析

3.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)对缓冲区进行一次性填充。

4.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,说明请求新的字符将会被阻塞,直接返回

5.2.3 readLine函数

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


6.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()函数是返回从当前的缓冲位置,到换行符之间的字符串,即从缓冲区当前位置开始的一行。


7.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

转载请注明:CodingBlog » 【Java语言8源码解析】IO包-Reader、BufferedReader与Scanner深度总结

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情