一、字节流读取中文时出现的问题:
文件中有中文时,用字节流读取会出现乱码的问题,因为一个中文为两个字节。
二、字符编码表
编码表:其实就是生活中字符和计算机二进制的对应关系表。
1、ascii: 一个字节中的7位就可以表示。对应的字节都是正数。0-xxxxxxx
2、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
3、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节是负数,第二个字节可能是正数
GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
GB18030:最新的中文码表,目前还没有正式使用。
4、unicode:国际标准码表:无论是什么文字,都用两个字节存储。
Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
5、UTF-8:基于unicode,一个字节就可以存储数据,不要用两个字节存储,而且这个码表更加的标准化,在每一个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正因为识别中文码表不唯一,涉及到了编码解码问题。
对于我们开发而言,常见的编码 GBK UTF-8 ISO-8859-1
文字--->(数字) :编码。 “abc”.getBytes() byte[] //数字就是字节
(数字)--->文字 : 解码。 byte[] b={97,98,99} new String(b) 用new String 传一个字节数组
三、字符输入流 Reader类
Reader类:读取字符流的超类。
方法:
1.read():读取单个字符并返回
2.read():将数据读取到数组中,并返回读取的个数
四、字符输入类 FileReader类
Reader类的子类,用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。
构造方法:FileReader fr=new FileReader(文件路径/文件对象);
package com.oracle.demo01;import java.io.FileReader;import java.io.IOException;//字符输入流public class ReaderDemo { //因为是一个字符一个字符读取的,所以读取中文不会出现乱码,专门用来处理文本文件 public static void main(String[] args) throws IOException { // 1.获取数据源 FileReader fr=new FileReader("d:\\java\\writer.txt"); //2.读取数据 int len=0; while((len=fr.read())!=-1){ System.out.println((char)len); } fr.close(); }}
五、字符输出流 Writer类
Writer是写入字符流的抽象类。其中描述了相应的写的动作。
方法:
六、字符输出类 FileWriter类
Writer类的子类,用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。
构造方法:FileWriter fw=new FileWriter(文件路径/文件对象);
package com.oracle.demo01;import java.io.FileWriter;import java.io.IOException;//字符输出流 public class WriteDemo { //字符流处理的是文本文件 public static void main(String[] args) throws IOException { // 文本文件:打开人能看懂的就是文本文件 //1.确定目的地 FileWriter fw=new FileWriter("d:\\java\\writer.txt",true); //2.写入数据 //fw.write(100); // 如果不刷新,会保存在内存中// char[] ch={'猴','赛','雷'}; //字符数组// fw.write(ch,0,2); //可以输出字符数组,也可以控制输出的长度 fw.write("崔永元实话实说",0,3); //单引号是 字符,双引号是字符串 //3.刷新 fw.flush(); //4.释放资源 fw.close(); }}
flush()方法与colse()方法的区别:
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用,还可以继续输出。
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后再关闭流。关闭后流不可以使用。
注意:如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
练习题:用字符流复制文件
package com.oracle.demo01;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;//用字符流复制文本文件,不要用来复制图片与视频等public class Copy { public static void main(String[] args) throws IOException { // 1.获取数据源 FileReader fr=new FileReader("e:\\java\\outint.PNG"); //2.获取目标 FileWriter fw=new FileWriter("D:\\java\\outin.png"); //3.读取并写入 int len =0; char[] ch=new char[1024]; while((len=fr.read(ch))!=-1){ fw.write(ch,0,len); } fw.flush(); fr.close(); fw.close(); }}
七、转换流
如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter。比如,如果文件内为UTF-8格式,但是电脑系统默认的格式为GBK格式,直接读取的话会出现读取内容与文件内容不符的问题,这个时候就需要转换流来转换。
7.1输出转换类OutputStreamWriter类
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的字符编码表,将要写入流中的字符编码成字节。它的作用的就是,将字符串按照指定的编码表转成字节,再使用字节流将这些字节写出去。
package com.oracle.demo01;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;//转换流 输出public class OutputStreamDemo { //结尾为Streatm的为字节流 结尾为reader或writer的为字符流 public static void main(String[] args) throws IOException { //1.确定目的地 //字节流输出对象 FileOutputStream fos=new FileOutputStream("e:\\java\\writer.txt",true); //2.设置走哪个码表 码表是文本文件内容所使用的码表 OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8"); //3.写入数据 //写入时就转换为了指定码表的字符流 osw.write("野猪佩奇"); osw.flush(); osw.close(); }}
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
7.2转换输入流 InputStreamReader类
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
package com.oracle.demo01;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;public class InputStreamDemo {//转换流 先走码表,根据码表解析,然后再进行读写操作 public static void main(String[] args) throws IOException { //1.获取数据源 FileInputStream fis=new FileInputStream("e:\\java\\writer.txt"); //2.设置走哪个码表 InputStreamReader isr=new InputStreamReader(fis,"utf-8"); //3.读取数据 int len=0; while((len=isr.read())!=-1){ System.out.print((char)len); } isr.close(); }}
注意:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象。
7.3转换流和字符类区别
发现有如下继承关系:
父类:OutputStreamWriter:
子类:FileWriter:
父类:InputStreamReader:
子类:FileReader;
OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
这三句代码的功能是一样的,其中第三句最为便捷。
注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。什么时候用子类呢?
条件:
1、操作的是文件。2、使用默认编码。
总结:
字节--->字符 : 看不懂的--->看的懂的。 需要读。输入流。 InputStreamReader
字符--->字节 : 看的懂的--->看不懂的。 需要写。输出流。 OutputStreamWriter
练习题:
package com.oracle.demo01;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;//转换流复制文本文件public class CopyTwo { public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("e:\\java\\writer.txt"); InputStreamReader osr=new InputStreamReader(fis,"utf-8"); FileOutputStream fos=new FileOutputStream("d:\\java\\writer.txt"); OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8"); int len=0; while((len=osr.read())!=-1){ osw.write(len); } osr.close(); osw.flush(); osw.close(); } }