[疯狂Java]I/O:转换流、BufferedReader简介、回推输入流

时间:2021-08-02 11:55:45

1. 将字节流转换成字符流:

    1) Java只提供了将字节流转换成字符流的方法,但是并没有提供字符流转换成字节流的方法。这是因为字符流要比字节流操作更加方便、直观,毕竟字符是人直接能看得懂的;

!!因此,如果一个流已经是字符流了,那就没有必要再转换成字节流了,毕竟字符流操作比字节流方便啊!而将字节流转换成字符流的目的就是为了操作更方便,所以Java不提供字符流到字节流的转换;

    2) 完成这个工作只需要InputStreamReader类就行了,其实该类名的全称应该是InputStream to Reader,即把InputStream转化成Reader的意思;

    3) InputStreamReader的API源码:

public class InputStreamReader extends Reader {

private final StreamDecoder sd;

/**
* Creates an InputStreamReader that uses the default charset.
*
* @param in An InputStream
*/
public InputStreamReader(InputStream in) {
!!可以看到,它继承的就是Reader,而构造器所包装的参数是一个InputStream,因此直接用构造器就能返回一个Reader对象,因此转换直接用构造器来实现;

    4) 示例:Reader r = InputStreamReader(System.in);   // System.in原本是InputStream的字节流,现在被转换成字符流了

    5) 同样OutputStreamWriter可以将输出字节流转换成输出字符流,其构造器:OutputStreamWriter(OutputStream out);  // 同样使用构造器完成转换

    6) 示例:从标准输入读取并处理

public class Test {

public static void main(String[] args) throws IOException {
try (
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(reader);
) {
String line = null;
while ((line = br.readLine()) != null) {
if (line.equals("exit")) {
System.exit(1);
}
System.out.println("Input: " + line);
}
}
}
}
!这里我们使用了BufferedReader对字符输入流reader进行了一层包装,以为BufferedReader(处理流)具有缓冲功能,可以实现整行读取,读取效率也很高;


2. BufferedReader简介:
    1) 是一种高级处理流,具有缓冲功能,可以整行读取流中的数据,读取效率更高(因为可以缓冲);

    2) 构造器:BufferedReader(Reader in); // 用一个低级节点流来构造

    3) 整行读取:String readLine(); // 以行结束符\n(Unix)、\r\n(Win)为一行读取数据,返回的字符串将去掉行结束符

!!如果读不到行结束符程序就会阻塞,这可以从上面程序可以看出,输完一行会停在那里等待下一行的输入(回车表示一行的输入);


3. 回推输入流:

    1) 即用输入流读取数据的时候可以将数据回推到输入流中的输入流;

!!输出流没有回推,以为输出是往外输出,没有回推反复输出的需求;

    2) 是一种高级处理流,可以包装输入节点流(即对各种不同的节点流的缓冲进行回推);

    3) Java的回推输入流类是PushbackInputStream和PushbackReader;

    4) 回推模型:

         i. 回推输入流具有两个缓冲区,一个是正常的输入流缓冲区,另一个是回推缓冲区;

         ii. 程序正常用read系列读取数据是必须先读取回推缓冲区的数据,只有当回推缓冲区读完后才能继续读取正常输入缓冲区中的数据;

         iii. 从这两个缓冲区中读取数据时都是读取一个少一个,即直接从缓冲区中“取走”数据,取走的数据就不再在缓冲区中了;

         iv. 而往回推缓冲区中回推数据时可以回推任何数据,因此只有将从输入缓冲区中读取的数据回推到回推缓冲区时才能实现对输入数据的反复读取;

!小结:回推缓冲区和输入缓冲区是隔开的,只不过回推缓冲区的数据会先读;

    5) 回推三方法:byte和char分别是字节流版本和字符流版本

         i. void unread(int b/int c);  // 回推一个单位b/c

         ii. void unread(byte[] buf/char[] cbuf);  // 回推一整个数组buf/cbuf

         iii. void unread(byte[] buf/char[] cbuf, int off, int len);  // 将数组从off开始的len个单位回推

!!可以看到和read一模一样,只不过干的是和read完全相反;

    6) 在创建回推输入流时需要指定回推缓冲区的大小(单位):PushbackReader(Reader in, int size);  // 如果回推超过size个单位会直接抛出异常!

    6) 示例:找出输入流中的字符串"Peter",并将该字符串之前的全部字符打印出来

public class Test {

public static void main(String[] args) throws IOException {
try (PushbackReader pr = new PushbackReader(new InputStreamReader(System.in), 64)) {
char[] buf = new char[32]; // 临时读取数据的缓冲
String preCon = ""; // 上一次读取的内容
int hasRead = 0;
int find = 0; // 找到目标字符串的位置
while ((hasRead = pr.read(buf)) > 0) {
String curCon = new String(buf, 0, hasRead); // 当前读取的内容
String con = preCon + curCon; // 由于目标字符串可能加载上一次和当前内容的中间,所以需要合并
find = con.indexOf("Peter"); // 检测是否找到目标字符串
if (find > 0) { // 如果找到了
pr.unread(con.toCharArray()); // 先将合并内容回推
char[] find_buf = new char[find]; // 目标字符串可能完全在curCon之中,因此find可能大于临时缓冲区大小32
pr.read(find_buf, 0, find); // 然后再从回推缓冲区读取到目标字符串之前的位置
System.out.println(new String(find_buf, 0, find)); // 打印退出
System.exit(0);
}
else { // 不存在,因此先打印上次内容
System.out.print(preCon); // 但是curCon中也许存在目标字符串的部分内容,因此curCon不能打印
preCon = curCon; // 递推
}
}
}
}
}
!!可以看到回推输入流最主要是用来对输入流进行分析!!而不是闹着好玩儿瞎回推!!