既然字节流可以操作所有文件,那么为什么还要学习字符流?
如果利用字节流,把文本文件中的中文,读取到内存中,有可能出现乱码
如果利用字节流,把中文写到文本文件中,也有可能出现乱码
字符流的介绍
字符流 = 字节流 + 编码表
中文的字节存储方式
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
基础知识:
计算机中存储的信息都是用二进制数表示的
按照某种规则,将字符变成二进制,在存储到计算机中,称为编码。
按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码。
编码和解码的方式必须一致,否则会导致乱码。
简单理解:
存储一个字符a,首先需在码表中查到对应的数字时97,然后转换成二进制进行存储。
读取的时候,先把二进制解析出来,再转成97,通过97查找对应的字符时a。
什么是字符集
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
常见的字符集
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等,一个中文以两个字节的形式存储,但不包含世界上各国的文字。
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
相关方法
| 方法名 | 说明 | 
|---|---|
| byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 | 
| byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 | 
| String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 | 
| String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 | 
代码演示
package com.itheima.charstream1;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class CharStreamDemo2 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //method1();
        // String(byte[] bytes):
        //通过使用平台的默认字符集解码指定的字节数组来构造新的 String
        // String(byte[] bytes, String charsetName):
        //通过指定的字符集解码指定的字节数组来构造新的 String
        //UTF-8
        byte [] bytes1 = {-23, -69, -111, -23, -87, -84, -25, -88, -117, -27, -70, -113, -27, -111, -104};
        //gbk
        byte [] bytes2 = {-70, -38, -62, -19, -77, -52, -48, -14, -44, -79};
        //利用默认的UTF-8进行解码
        String s1 = new String(bytes1);
        System.out.println(s1);//黑马程序员
        //利用指定的GBK进行解码
        String s2 = new String(bytes2,"gbk");
        System.out.println(s2);//黑马程序员
    }
    private static void method1() throws UnsupportedEncodingException {
        // byte[] getBytes():
        //使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
        // byte[] getBytes(String charsetName):
        //使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
        String s = "黑马程序员";
        //利用idea默认的UTF-8将中文编码为一系列的字节
        byte[] bytes1 = s.getBytes();
        System.out.println(Arrays.toString(bytes1));
        //byte[] bytes2 = s.getBytes("UTF-8");
        byte[] bytes2 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));
    }
}
为什么字节流读取文本文件,可能会出现乱码?
因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。
字符流读取中文的过程
字符流=字节流+编码表
基础知识:
不管是在哪张码表当中,中文的第一个字节一定是负数。
小结:
1.想要进行拷贝,一律使用字节流或者字节缓冲流
2.想要把文本文件中的数据读到内存中,请使用字符输入流。
想要把内存中的数据写到文本文件中,请使用字符输出流。
3.GBK码表中一个中文两个字节,UTF-8编码格式一个中文3个字节
介绍
Writer: 用于写入字符流的抽象父类
FileWriter: 用于写入字符流的常用子类
构造方法
| 方法名 | 说明 | 
|---|---|
| FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象、 | 
| FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象 | 
| FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 | 
| FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 | 
| 方法名 | 说明 | 
|---|---|
| void write(int c) | 写一个字符 | 
| void write(char[] cbuf) | 写出一个字符数组 | 
| void write(char[] cbuf, int off, int len) | 写出字符数组的一部分 | 
| void write(String str) | 写一个字符串 | 
| void write(String str, int off, int len) | 
| 方法名 | 说明 | 
|---|---|
| flush() | 刷新流,之后还可以继续写数据 | 
| close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 | 
package com.itheima.charstream1;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo3 {
    //创建字符输出流的对象
    public static void main(String[] args) throws IOException {
        //FileWriter fw=new FileWriter(new File("charstream\\a.txt"));
        FileWriter fw=new FileWriter("charstream\\a.txt");
        /*//写出数据
        //void write(int c)	写一个字符
        fw.write(97);
        fw.write(98);
        fw.write(99);*/
       /* //void write(char[] cbuf)	写出一个字符数组
        char[] chars={97,98,99,100,101};
        fw.write(chars);*/
        /*//void write(char[] cbuf, int off, int len)	写出字符数组的一部分
        char[] chars={97,98,99,100,101};
        fw.write(chars,0,3);*/
      /*  //void write(String str)	写一个字符串
        String line="黑马程序员abc";
        fw.write(line);*/
        //void write(String str, int off, int len)	写一个字符串的一部分
        String line="黑马程序员abc";
        fw.write(line,0,2);
        fw.close();
    }
}
步骤:
1.创建字符输出流对象
注意事项:
如果文件不存在,就创建,但是要保证父级路径存在。
如果文件存在就清空
2.写数据
注意事项:
写出int类型的整数,实际写出的是整数在码表上对应的字母。
写出字符串数据,是把字符串本身原样写出。
3.释放资源
注意事项:
每次使用完流必须要释放资源
原文:https://www.cnblogs.com/faded8679/p/13944446.html