概念

在读取数据的时候,一次可以读取一个字符 ,所以读取中文比较方便;
区别于字节流而言,字节流操作的是字节为单位,而这里字符流是以字符为单位!字节流:除了可以操作文本文件,还可以操作二进制文件;字符流,只能操作文本文件!
所有字符流都继承于抽象类Reader、Writer ,Reader 是咱的输入流,而Writer是咱的输出流!

字符流的起源

当我们使用字节流复制文本文件时,文本文件也会存在中英文,但是之前案例中没有任何问题,原因是计算机的存储最终存储的底层操作会自动进行字节拼接,根据系统默认的规则拼接成特定的中文。那么计算机是如何识别中文字符呢?
汉字在存储时,无论选择哪种编码,中文字符的第一个字节都是负数,所以计算机解码时可以识别
因为字节流操作中文很不方便, 所以Java就提供了字符流
字符流 = 字节流 + 编码表

字符串编码问题

1、编码表

  • 计算机中存储的信息都是用二进制数来表示的,我们在屏幕上看到的英文,汉字和符号等字符都是二进制经过转换后的结果
  • 按照某种规则,将字符存储到计算机中,称之为编码过程。反之,将存储在计算机中的二进制数据按照某种规则解析显示出来,我们称之为解码过程。
  • 通过xxx编码存储的数据,必须通过xxx编码来解析,这样才能显示正确的内容,否则就会导致字符乱码,获取到错误的信息
  • 字符编码的本质就是一套自然规则的表与二进制数之间的一种对应规范

    2、ASCII

    ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。
    基本的ASCII字符集,使用7位二进制表示一个字符,共128字符,SACII的扩展字符集使用8位表示一个字符,共256个字符,方便支持欧洲常用的字符,是一个系统支持的所有字符的集合,包括各国国家文字,标点符号,图形符号,数字等。

    3、GBxxx字符集

  • GB2312

简体中文码表,一个小于127的字符的意义与原来相同,但是将两个大于127的字符连接在一起,就表示成一个中文汉字,这样的组合大约包含了7000多个简体汉字,此外数学符号,罗马希腊的字母,数字,日文都编译在里面。另外,把ASCII中的数字,字母,符号都重新编译成两个字节长度的编码,这就是我们输入法常用的“全角”字符,而原来的127内的字符都是”半角”字符。

  • GBK

最常用的中文码表,在GB2312标准的基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时还支持繁体汉字及日韩文字等。

  • GB18030

最新的中文码表,共收录70244个汉字,采用多字节方式编码,每个字符可以由1个,2个,3个,4个字节组成。支持中国少数名族的文字,同时支持繁体汉字和日韩文字等

4、Unicode字符集

为表达任意语言的任意字符设计的编码,是业界的统一编制,也成为统一码,标准万国码。它最多使用4个字节的数字来表示每个字母,符号或者文字。有三种编码方案: UTF-8\ UTF-16 \ UTF-32.最常使用的就是UTF-8编码。

  • UTF-8

用来表示Unicode标准中的任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码,互联网工程小组IETF 要求所有互联网协议都必须支持 UTF-8 编码。它使用1~4个字节为每个字符实现编码。具体编码规则如下:

  • 128个US-ASCII字符,只需要一个字节编码
  • 拉丁文等字符,需要两个字节编码
  • 大部分常用字(含中文),采用三个字节编码
  • 其他极少数使用Unicode的辅助字符,使用四个字节编码

    字符输入流

    只能度文本文件 ,不能度二进制数据 (图片、音频、视频)
    JDK中提供的字符输入流,可以按照字符进行读写 ,所有字符输入流,都有一个抽象父类 :Reader
    字符流 - 图1
    下面就以 FileReader 为例 :
    字符流 - 图2
    核心API方法 :
    字符流 - 图3
    public class TestReader {
    public static void main(String[] args) {
    //E:/woniu/57/JavaSE/day19/Hello.java
    Reader reader = null;
    File f = new File(“E:/woniu/57/JavaSE/day19/Hello.java”);
    try {
    //reader = new FileReader(“E:/woniu/57/JavaSE/day19/Hello.java”);
    reader = new FileReader(f);

    //1. 一个字符一个字符的读取
    /int i = reader.read();
    while(i !=-1){
    System.out.print((char)i);
    i = reader.read();
    }
    /
    //2.把字符读到 数组中
    /char[] chars = new char[(int) f.length()];
    reader.read(chars);
    String s = new String(chars);

    System.out.println(s);
    /

    //3.指定数组大小
    char[] chars = new char[10];
    int len = reader.read(chars, 0, chars.length);
    while(len != -1){
    String s = new String(chars,0,len);
    System.out.print(s);

    len = reader.read(chars,0,len);
    }
    } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }finally {
    try {
    reader.close();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
    }

    字符输出流

    只能输出字符 ,不能操作二进制 (图片、音频、视频)
    JDK提供的字符输出流 ,都有一个抽象父类 :Writer
    字符流 - 图4
    下面我们以FileWriter为例 :
    字符流 - 图5
    核心API 方法 :
    字符流 - 图6
    public class TestWiter {
    public static void main(String[] args) {
    String s = “OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集”;
    //把这个字符串 写到 E:/woniu/57/JavaSE/day19/a.txt

    //使用的是 字符流 需要字符串转成字符数组
    char[] charArray = s.toCharArray();

    File f = new File(“E:/woniu/57/JavaSE/day19/a.txt”);
    if(!f.exists()){
    try {
    f.createNewFile();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    //写出
    Writer w = null;

    try {
    w = new FileWriter(f);
    //1 .按照字符写出
    /for(int i=0;i w.write(charArray[i]);
    }
    /

    //2.按照数组写出
    /w.write(charArray);
    //刷新 缓冲区 flush() close()
    w.flush();
    Thread.sleep(50000); //程序停止 50秒
    /

    //3 .指定输出数组的大小
    /w.write(charArray, 0, charArray.length);
    w.flush();
    /

    //一次性把字符串 写出去
    w.write(s);

    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } finally {
    try {
    w.close();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
    }
    把Hello.java 从一个位置 复制 到另外一个位置 使用 字符流
    public class TestCharCopy {
    public static void main(String[] args) {

    String path = “E:/woniu/57/JavaSE/day19/Hello.java”;
    File inFile = new File(path);

    File outFile = new File(“C:/Hello.java”);
    if(!outFile.exists()){
    try {
    outFile.createNewFile();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    Reader r = null;
    Writer w = null;


    try {
    r = new FileReader(inFile);
    w = new FileWriter(outFile);

    //1 . 一个一个 字符读 一个一个写
    /int i = r.read();
    while(i != -1){
    w.write((char)i);
    i = r.read();
    }
    /

    //2.一次性读和写
    /char[] cs = new char[(int) inFile.length()];
    int len = r.read(cs);
    w.write(cs);
    w.flush();
    /

    //3 读写指定大小 推荐
    char[] cs = new char[10];
    //方法的返回值 是读取到的字符长度
    int len = r.read(cs, 0, cs.length);
    while(len != -1){
    w.write(cs, 0, len); // 第三个参数 一定是 len
    len = r.read(cs, 0, cs.length);
    }

    } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }finally {
    try {
    r.close();
    w.close();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }
    }
    }