Fork me on GitHub

IO 流之字符流的缓冲区

  1. 缓冲区的出现提高了对数据的读写效率
  2. 对应类:
    1. BufferedWriter
    2. BufferedReader
  3. 缓冲区需要结合流才可以使用, 对流的功能进行了增强, 即对流的操作起到装饰作用

使用缓冲区实现文本文件的写入和读取

// 写入
public class BufferedWriterDemo{
    public static void main(String[] args) throws IOException {

        FileWriter fw = new FileWriter("buf.txt");

        // 为了提高写入的效率, 使用了字符流的缓冲区
        // 创建了一个字符写入流的缓冲对象, 并和指定要被缓冲的流对象相关联

        BufferedWriter bufw = new BufferedWriter(fw);

        // 使用缓冲区的写入方法将数据写入到缓冲区中
        bufw.write("abcde");

        // 换行, 其实就是封装了系统属性  line.separator
        // BufferedWriter 特有方法
        bufw.newLine();

        bufw.newline("hahaha");

        // 使用缓冲区的刷新方法将数据刷入到目的地中
        bufw.flush();

        // 关闭缓冲区, 其实关闭的就是被缓冲的流对象
        bufw.close();
    }
}

// 读取
public class BufferedReaderDemo {
    public static void main(String[] args){

        FileReader fr = new FileReader("buf.txt");
        BufferedReader bufr = new BufferedReader();

        /*  读取字符数组的方式
         *
         *  char[] buf = new char[1024];

         *  int len = 0;
         *  while((len=bufr.read(buf))!= -1){
         *  System.out.println(new String(buf,0,len));
         *  }
         */

         // 按一行读取, BufferredReader 特有方法: readLine()
         // 如果到达流末尾, 返回 null
         String line = null;
         while((line=bufr.readLine()) != null){  // 注意: 此处判断的是 != null
            System.out.println(line);
        }

        // 关闭缓冲区
        bufr.close();
    }
}

BufferedReader 中的 readLine() 方法原理

  • BufferedReader 类复写了父类的以下方法:
    • read(); : 读取单个字符
    • read(char[] buf, int off, int len); : 读取数组的一部分
  • BufferedReader 类自己特有的方法 readLine()
  • BufferedReader 类继承了父类方法:
    • read(char[] buf) : 将字符读入数组

readLine() 方法原理: 使用了读取缓冲区的 read() 方法, 将读取到的字符进行缓冲并判断换行标记. 将标记前的缓存数据变成字符返回.

模拟实现 BufferedReader

// 需求: 自定义的读取缓冲区, 其实就是模拟一个 BufferedReader

/* 分析:
 *    缓冲区中无非就是封装了一个数组, 并对外提供了更多的方法对数组进行访问
 *    其实这些方法最终操作的都是数组的角标
 *
 *     缓冲区原理: 其实就是从源中获取一批数据装进缓冲区中, 在从缓冲区中不断
 *     的取出一个一个数据.
 *     
 *     在缓冲区中数据此次取完后, 在从源中继续取一批数据进缓冲区, 当源中的数据取光时,
 *     用 -1 作为结束标记
 */

 public class MyBufferedReader{

    private FileReader r;

    // 定义一个数组作为缓冲区
    private char[] buf = new char[1024];

    // 定义一个指针用于操作这个数组中的元素, 当操作到最后一个元素时, 指针应该归 0
    private int pos = 0;

    // 定义一个计数器, 记录缓冲区中的数据个数. 当该数据减到0, 就从源中继续获取数据到缓冲区中
    private int count = 0;

    // 带参数的构造函数, 指定增强的流对象
    public MyBufferedReader(FileReader r){
        this.r = r;
    }

    // 定义从缓冲区读取单个字符的 read() 方法
    public int myRead() throws IOException{

        // 1. 从源中获取一批数据到缓冲区中, 需要先做判断, 只有计数器为 0 时, 才需要从源中获取数据
        /* if(count == 0){

         *   // 记录从源中获取的数据个数
         *   count = r.read(buf);

         *   if(count < 0){
         *      return -1;
         *  }

         *  // 每次从源中获取数据到缓冲区后, 角标归零
         *   pos = 0;

         *   // 获取第一个数据
         *   char ch = buf[pos];

         *   pos++;
         *   count--;

         *   return ch;
         *   } else {

         *   char ch = buf[pos];

         *   pos++;
         *   count--;

         *   return ch;
         *   }
         */
        // 代码优化:

        if(count == 0){
            count = r.read(buf);

            pos = 0;
        }

        if(count < 0){
            return -1;
        }

        char ch = buf[pos++];

        count--;

        return ch;
    }

    // 定义从缓冲区读取一行字符的 readLine() 方法
    public String myReadLine() throws IOException{

        // 定义一个数组容器, 存储从缓冲区中获取到的数据
        StringBuilder sb = new StringBuilder();

        // 使用自定义的 MyRead 方法, 从缓冲区中不断获取单个字符,
        int ch = 0;
        while((ch=myRead()) != -1){

            if(ch=='\r')
                continue;
            if(ch=='\n')
                return sb.toString();

            // 将从缓冲区中读到的字符, 存储到缓存行数据的缓冲区中
            sb.append((char)ch);
        }

        // 如果最后一行结尾没有回车符, 缓冲区的读取已结束,
        // 但是并不能判断 if(ch=='\n'), 所有最后一行未能输出
        if(sb.length() != 0)
            return sb.toString();

        return null;
    }

    public void myClose() throws IOException{
        r.close();
    }

    }


使用缓冲区的方式复制文本文件

public class CopyTextByBufTest {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("buf.txt");
        BufferedReader bufr = new BufferedReader(fr);

        FileWriter fw = new FileWriter("buf_copy.txt");
        BufferedWriter bufw = new BufferedWriter(fw);

        /*
         *  int ch = 0;
         *  // 使用缓冲区对象进行单个字符的读写
         *  // 这是直接从内存中读取单个字符, 不是从硬盘中
         *  while((ch=bufr.read()) != -1){
         *        bufw.write(ch);
         *    }
         */

         // 使用行进行读取文件

         String line = null;
         while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.newLine();
            bufw.flush();  // 注意: 每次写入之后, 进行刷新保存
        }

        bufw.close();
        bufr.close();
}
}

LineNumberReader 装饰类

  1. BufferedReader 的子类
  2. 可以跟踪行号的缓冲字符输入流
    • setLineNumber(int); : 设置当前行号
    • getLineNumber(); : 获取当前行号



参考资料

posted @ 2017-09-11 10:20  小a的软件思考  阅读(1397)  评论(0编辑  收藏  举报