一、为什么会出现字符流

由于字节流操作中文不是特别方便,所以java就提供了字符流
字符流=字节流+编码表

用字节流赋值文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

二、编码表

1、基础知识

计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照某种规则,将字符存储到计算机中,称为编码,反之,将存储在计算机中二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象。
字符编码:就是一套自然语言的字符与二进制数之间的对应规格(A,65)

2、字符集:

是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等
计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少由一套字符编码。
常见字符集由ASCll字符集,GBxxx字符集,Unicode字符集等

ASCll字符集

ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符

GBXXX字符集

GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。
基本集共收入汉字6763个和非汉字图形字符682个。整个字符集分成94个区,每区有94个位。每个区位上只有一个字符,因此可用所在的区和位来对汉字进行编码,称为区位码
把换算成十六进制的区位码加上2020H,就得到国标码。国标码加上8080H,就得到常用的计算机机内码。1995年又颁布了《汉字编码扩展规范》(GBK)。GBK与GB 2312—1980国家标准所对应的内码标准兼容,同时在字汇一级支持ISO/IEC10646—1和GB 13000—1的全部中、日、韩(CJK)汉字,共计20902字。

GBK 向下与 GB 2312 编码兼容,向上支持 ISO 10646.1国际标准,是前者向后者过渡过程中的一个承上启下的产物。ISO 10646 是国际标准化组织 ISO 公布的一个编码标准,即 Universal Multilpe-Octet Coded Character Set(简称UCS),大陆译为《通用多八位编码字符集》,台湾译为《广用多八位元编码字元集》,它与 Unicode 组织的 Unicode 编码完全兼容。ISO 10646.1 是该标准的第一部分《体系结构与基本多文种平面》。我国 1993 年以 GB 13000.1 国家标准的形式予以认可(即 GB 13000.1 等同于 ISO 10646.1)。
GBK编码,是在GB2312-80标准基础上的内码扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年10月制定, 1995年12月正式发布,中文版的WIN95、WIN98、WINDOWS NT以及WINDOWS 2000、WINDOWS XP、WIN 7等都支持GBK编码方案。

GB 18030,全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030-2005共收录汉字70,244个。

Unicode字符集

统一码,也叫万国码、单一码(Unicode)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式发布1.0版本,2020年发布13.0版本。

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件网页及其他存储或传送文字的应用中,优先采用的编码。

三、字符串中的编码解码问题

1、编码

byte[ ] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[ ] getBytes(String charsetName):使用指定的字符集将String编码为一系列字符,将结果存储到新的字节数组中

2、解码

String(byte[ ] bytes):通过使用平台默认字符集解码指定的字节数组来构造新的String
String(byte[ ] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String

四、字符流中的编码解码问题

1、字符流抽象基类

Reader:字符输入流的抽象类
Writer:字符输出流的抽象类

2、字符流中和编码解码问题相关的两个类

InputStreamReader
OutputStreamWriter

  1. public static void main(String[] args) throws IOException {
  2. // 分两步创建输出流
  3. FileOutputStream fileOut=new FileOutputStream("D:\\demo.txt");
  4. OutputStreamWriter out0=new OutputStreamWriter(fileOut);
  5. //使用默认的字符编码
  6. OutputStreamWriter out1=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));
  7. out1.write("中国");
  8. out1.close();
  9. //使用指定的字符编码 UTF-8
  10. OutputStreamWriter out=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"),"UTF-8");
  11. out.write("中国");
  12. out.close();
  13. //采用指定字符编码读取数据
  14. InputStreamReader in1=new InputStreamReader(new FileInputStream("D:\\demo.txt"),"UTF-8");
  15. //一次读取一个字节数据
  16. int ch;
  17. while ((ch=in1.read())!=-1){
  18. System.out.println((char)ch);
  19. }
  20. in1.close();
  21. }

五、字符流写数据的五种方式

/**
1、写一个字符
*/
public void write(int c) throws IOException {
        se.write(c);
}

/**
2、写一组字符
*/
public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
}

/**
3、写字符数组的一部分
*/
public void write(char cbuf[], int off, int len) throws IOException {
    se.write(cbuf, off, len);
}

/**
4、写一个字符串
*/
public void write(String str) throws IOException {
    write(str, 0, str.length());
}

/**
5、写字符串的一部分
*/
public void write(String str, int off, int len) throws IOException {
    se.write(str, off, len);
}
 OutputStreamWriter out1=new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));

        //写入数据方式一
        out1.write("97");
        out1.flush();
        out1.write("98");
        out1.close();//关闭流的时候,会先自动flush


        //写入数据方式二
        char[] chs = {'a','b','c'};
        out1.write(chs);
        out1.flush();

        //写入数据方式三
        out1.write(chs,0,chs.length);//abc
        out1.write(chs,1,2);//ab
        out1.flush();

        //写入数据方式四
        out1.write("abcde");
        out1.flush();

        //写入数据方式五
        out1.write("abcde",0,"abced".length());
        out1.flush();

六、字符流读数据的两种方式

/**
1、一次读一个字符
*/
public int read() throws IOException {
    return sd.read();
}

/**
2、一次读取一个字符组
*/
public int read(char cbuf[], int offset, int length) throws IOException {
    return sd.read(cbuf, offset, length);
}
        InputStreamReader in1=new InputStreamReader(new FileInputStream("D:\\demo.txt"),"UTF-8");

        //一次读取一个字节数据
        int ch;
        while ((ch=in1.read())!=-1){
            System.out.println((char)ch);
        }

        //一次读一组数据
        char[] chars=new char[1024];
        int len;
        while((len=in1.read(chars))!=-1){
            System.out.println(new String(chars,0,len));
        }

        in1.close();

七、FileReader和FileWriter

转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
FileReader:用于读取字符文件的便捷类
FileWriter:用于写入字符文件的便捷类

八、字符缓冲流

BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可用于大多数用途。

BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途。

1、构造方法

public BufferedWriter(Writer var1) {
    this(var1, defaultCharBufferSize);
}

public BufferedReader(Reader var1) {
    this(var1, defaultCharBufferSize);
}

2、使用

public static void main(String[] args) throws IOException {
        BufferedWriter rd=new BufferedWriter(new FileWriter("D:\\demo.txt"));
        rd.write("hello");
        rd.write("hello");
        rd.close();


        BufferedReader br=new BufferedReader(new FileReader("D:\\demo.txt"));
        //一次读一个字符数据
        int len;
        while ((len=br.read())!=-1){
            System.out.println((char)len);
        }

        br.close();

        //一次读取一组字节数据
        char[] chs=new char[1024];
        while ((len=br.read(chs))!=-1){
            System.out.println(new String(chs,0,len));
        }
    }

3、字符缓冲流的特有功能
BufferedWriter

//写一行行分隔符,行分隔符字符串由系统属性定义
public void newLine() throws IOException {
    this.write(this.lineSeparator);
}

BufferedReader

//读一行文字。结果包含行的内容的字符串,不包括任何终止字符,如果流的结果已经达到,则为null
public String readLine() throws IOException {
    return this.readLine(false);
}
    public static void main(String[] args) throws IOException {
        BufferedWriter rd=new BufferedWriter(new FileWriter("D:\\demo.txt"));
        rd.write("hello");
        rd.newLine();//换行
        rd.write("hello");
        rd.newLine();//换行
        rd.close();


        BufferedReader br=new BufferedReader(new FileReader("D:\\demo.txt"));
        //读取一行数据
        String line;
        while ((line=br.readLine())!=null){
            System.out.println(line);//因为读取出来不包括换行符,需要手动换行
        }
        br.close();
    }

九、IO流小节

点击查看【processon】