IO 流之字符流的缓冲区

时间:2021-05-26 21:00:00
  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) : 将字符读入数组

IO 流之字符流的缓冲区

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(); : 获取当前行号



参考资料