缓冲流:如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等 转换流:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节

缓冲流

缓冲概述

缓冲流,也叫高效流,是对4个基本的 FileXXXX 流的增强,所以也是4个流,按照数据类型分类

缓冲输入流 缓冲输出流
字节**流** BufferedInputStream
字节输入流
BufferedOutputStream
字节输出流
字符**流** BufferedReader
字符输入流
BufferedWriter
字符输出流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO 次数,从而提高读写的效率

BufferedInputStream 字节缓冲输入流

  1. java.io.BufferedInputStream extends InputStream
  2. 继承自父类的成员方法
  3. read close
  4. 构造方法
  5. 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  6. BufferedInputStream(InputStream in)
  7. 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  8. BufferedInputStream(InputStream in, int size)

字节缓冲输入流.png

BufferedOutputStream 字节缓冲输出流

  1. java.io.BufferedOutputStream extends OutputStream
  2. 继承自父类的共性成员方法
  3. write flush close
  4. 构造方法
  5. 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
  6. BufferedOutputStream(OutputStream out)
  7. 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
  8. BufferedOutputStream(OutputStream out, int size)

字节缓冲输出流.png

测试复制文件

  1. import java.io.*;
  2. public class BufferedStreamTest {
  3. public static void main(String[] args) throws IOException {
  4. FileInputStream fis = null;
  5. FileOutputStream fos = null;
  6. BufferedInputStream bis = null;
  7. BufferedOutputStream bos = null;
  8. long start = System.currentTimeMillis();
  9. try {
  10. fis = new FileInputStream("./test.txt"); // 创建字节输入流
  11. fos = new FileOutputStream("./newTest.txt"); // 创建字节输出流
  12. bis = new BufferedInputStream(fis); // 创建字节缓存输入流
  13. bos = new BufferedOutputStream(fos); // 创建字节缓存输出流
  14. byte[] b = new byte[1024];
  15. int len = 0;
  16. while ((len = bis.read(b)) > 0) { // 循环从缓存流中读取数据
  17. bos.write(b, 0, len); // 向缓存流中写入数据,读取多少写入多少
  18. }
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. } finally {
  22. bis.close();
  23. bos.close();
  24. }
  25. long end = System.currentTimeMillis();
  26. System.out.println("测试时间==" + (end - start)); //耗时20毫秒
  27. }
  28. }

上面代码中我们使用了缓存流和文件流,但是我们只关闭了缓存流。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流

BufferedReader 字符缓冲输入流

  1. java.io.BufferedWriter extends Reader
  2. 继承自父类的共性成员方法
  3. 读取单个字符并返回
  4. int read()
  5. 一次读取多个字符,将字符读入数组
  6. int read(char[] cbuf)
  7. 关闭该流并释放与之关联的所有资源
  8. void close()
  9. 构造方法
  10. 创建一个使用默认大小输入缓冲区的缓冲字符输入流
  11. BufferedReader(Reader in)
  12. 创建一个使用指定大小输入缓冲区的缓冲字符输入流
  13. BufferedReader(Reader in, int size)
  14. 读(返回)一行文字,读取数据,遇到的终止符(\n\r换行回车)
  15. 包含读取内容的数据(一定不包含回车换行),到达流末尾则返回null
  16. public String readLine()

字符缓冲输入流.png

BufferedWriter 字符缓冲输出流

  1. java.io.BufferedWriter extends Writer
  2. 继承自父类的共性成员方法
  3. void write(int c) 写入单个字符
  4. void write(char[] cbuf)写入字符数组
  5. abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
  6. void write(String str)写入字符串
  7. void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  8. void flush()刷新该流的缓冲
  9. void close() 关闭此流,但要先刷新它
  10. 构造方法
  11. 创建一个使用默认大小输出缓冲区的缓冲字符输出流
  12. BufferedWriter(Writer out)
  13. 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
  14. BufferedWriter(Writer out, int sz)

字符缓冲输出流.png

文本排序

in.txt

  1. 3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
  2. 8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
  3. 4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
  4. 2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
  5. 1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
  6. 9.今当远离,临表涕零,不知所言。
  7. 6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
  8. 7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
  9. 5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
  1. import java.io.*;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class Mian{
  5. public static void main(String[] args) throws IOException {
  6. BufferedReader br = new BufferedReader(new FileReader("in.txt"));
  7. BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
  8. HashMap<Object, Object> hm = new HashMap<>();
  9. String len;
  10. while ((len=br.readLine())!=null){
  11. String[] arr = len.split("\\.");
  12. hm.put(arr[0],arr[1]);
  13. }
  14. for (Map.Entry<Object, Object> enters : hm.entrySet()) {
  15. bw.write(enters.getKey()+"."+enters.getValue());
  16. bw.newLine();
  17. }
  18. bw.close();
  19. br.close();
  20. }
  21. }

字符编码和字符集

计算机中储存的信息都是用二进制数表示的

  • 编码: 按照某种规则,将字符转化成二进制存储到计算机中
  • 解码: 将存储在计算机中的二进制数按照 某种规则解析显示出来


字符编码 Character Encoding

就是一套自然语言的字符与二进制数之间的对应规则

字符集 Charset

也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
字符集 Charset.png

  • ASCII字符集
  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)
  • 基于拉丁 字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显 示字符(英文大小写字符、阿拉伯数字和西文符号
  • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits) 表示一个字符,共256字符,方便支持欧洲常用字符

  • [x] ISO-8859-1字符集

  • 拉丁码表,别名Latin-1
  • 用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。 ISO-5559-1使用单字节编码,兼容ASCII编码

  • [x] GBxxx字符集

GB就是国标的意思,是为了显示中文而设计的一套字符集

  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时, 就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文 的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这 就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了
  • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了 21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
  • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节 组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等

  • [x] Unicode字符集

  • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码
  • 它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码
  • 可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以, 我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则
    1. 128个US-ASCII字符,只需1个字节编码
    2. 拉丁文等字符,需要2个字节编码
    3. 大部分常用字(含中文),使用3个字节编码
    4. 其他极少使用的Unicode辅助字符,使用4字节编码

转换流

转换流是字节与字符间的桥梁
转换流是字节与字符间的桥梁.png

编码引出的问题

编码引出的问题1.png编码引出的问题2.png
那么如何读写取GBK编码的文件呢?

  • InputStreamReader
  • OutputStreamWriter

InputStreamReader 字节流通向字符流的桥梁

  1. 继承自父类的共性成员方法
  2. int read() 读取单个字符并返回
  3. int read(char[] cbuf)一次读取多个字符,将字符读入数组
  4. void close() 关闭该流并释放与之关联的所有资源
  5. 构造方法
  6. 创建一个使用默认字符集的 InputStreamReader
  7. InputStreamReader(InputStream in)
  8. 创建使用指定字符集的 InputStreamReader
  9. //InputStream in:字节输入流,用来读取文件中保存的字节
  10. //String charsetName:指定的编码表名称,不区分大小写,
  11. //可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8
  12. InputStreamReader(InputStream in, String charsetName)

InputStreamReader.png

OutputStreamWriter 字符流通向字节流的桥梁

  1. java.io.OutputStreamWriter extends Writer
  2. 继承自父类的共性成员方法
  3. void write(int c) 写入单个字符。
  4. void write(char[] cbuf)写入字符数组。
  5. abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
  6. void write(String str)写入字符串。
  7. void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  8. void flush()刷新该流的缓冲。
  9. void close() 关闭此流,但要先刷新它
  10. 构造方法
  11. 创建使用默认字符编码的 OutputStreamWriter
  12. OutputStreamWriter(OutputStream out)
  13. 创建使用指定字符集的 OutputStreamWriter
  14. //OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
  15. //String charsetName:指定的编码表名称,不区分大小写,
  16. //可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8
  17. OutputStreamWriter(OutputStream out, String charsetName)

OutputStreamWriter.png

将GBK编码的文本文件转换为UTF-8编码的文本文

import java.io.*;
public class Mian {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("uft-8.txt"));
        char[] chars=new char[1024];
        int len;
        while ((len=isr.read(chars))!=-1){
            osw.write(chars,0,len);
        }
        isr.close();
        osw.close();
    }
}

以获取键盘输入为例来介绍转换流的用法

java使用System.in代表输入。即键盘输入,但这个标准输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容,所以可以使用InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容,如下代码所示:

import java.io.*;

public class InputStreamReaderTest {
  public static void main(String[] args) throws IOException {
    try {
      InputStreamReader reader = new InputStreamReader(System.in);// 将System.in对象转化为Reader对象
      BufferedReader bufferedReader = new BufferedReader(reader); // 将普通的Reader包装成BufferedReader
      String buffer = null;
      while ((buffer = bufferedReader.readLine()) != null) {
        if (buffer.equals("exit")) System.exit(1);// 如果读取到的字符串为“exit”,则程序退出
        System.out.print("输入内容:" + buffer); // 打印读取的内容
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
    }
  }
}

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,它可以一次读取一行文本——以换行符为标志,如果它没有读到换行符,则程序堵塞。等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容。