字符流
在我们复制文件的时候,使用字节流方式复制的文件会出现乱码现象,这是由字符的编码方式决定的。这时候我们想复制带有中文的文件,就要用到字符流的方式。
字符输入流Reader
我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节。只要把这些字节去查阅对应的编码表,就能够得到与之对应的字符。API中是否给我们已经提供了读取相应字符的功能流对象,Reader,读取字符流的抽象超类。
read():读取单个字符并返回
read(char[]):将数据读取到数组中,并返回读取的个数。
FileReader读取包含中文的文件
public class CharStreamDemo { public static void main(String[] args) throws IOException { //给文件中写中文 writeCNText(); //读取文件中的中文 readCNText(); } //读取中文 public static void readCNText() throws IOException { FileReader fr = new FileReader("D:\\test\\cn.txt"); int ch = 0; while((ch = fr.read())!=-1){ //输出的字符对应的编码值 System.out.println(ch); //输出字符本身 System.out.println((char)ch); } } //写中文 public static void writeCNText() throws IOException { FileOutputStream fos = new FileOutputStream("D:\\test\\cn.txt"); fos.write("JAVA是世界上最好的语言".getBytes()); fos.close(); } }
字符输出流Writer
package com.oracle.demo01; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* * 字符流:专门针对于文本文件的 * 除了文本文件都可以用字节流 * * 怎么区分字节流和字符流 * 类名中带reader和writer的都是字符流 * 类名中带Stream的都是字节流 * */ public class WriteDemo { public static void main(String[] args) throws IOException { //read(); //read2(); writer(); } public static void read() throws IOException { FileReader fr=new FileReader("d:\\test\\wenjian.txt"); int len=0; //read()方法也是一次只读一个字符 while((len=fr.read())!=-1) { System.out.print((char)len); } fr.close(); } public static void read2() throws IOException { FileReader fr=new FileReader("d:\\test\\wenjian.txt"); int len=0; char[] ch=new char[1024]; while((len=fr.read(ch))!=-1) { System.out.print(new String(ch,0,len)); } fr.close(); } public static void writer() throws IOException { FileWriter fw=new FileWriter("d:\\test\\wenjian.txt",true); fw.write(100); fw.flush();//刷新,调用write方法后,要刷新才能把字符写进去 fw.write("中午吃啥"); fw.flush(); char[] ch= {'I','L','O','V','E','J','A','V','A'}; fw.write(ch); fw.close();//具有刷新一次的功能,然后再关闭 } }
flush()和close()的区别
flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用。
close():关闭资源,但在关闭前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后在关闭流。流不可以使用。如果写入数据多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭。
字符流练习:复制文本文件
public class CopyTextFileTest { public static void main(String[] args) throws IOException { copyTextFile(); } public static void copyTextFile() throws IOException { //1,明确源和目的。 FileReader fr = new FileReader("c:\\cn.txt"); FileWriter fw = new FileWriter("c:\\copy.txt"); //2,为了提高效率。自定义缓冲区数组。字符数组。 char[] buf = new char[1024]; int len = 0; while((len=fr.read(buf))!=-1){ fw.write(buf,0,len); } /*2,循环读写操作。效率低。 int ch = 0; while((ch=fr.read())!=-1){ fw.write(ch); } */ //3,关闭资源。 fw.close(); fr.close(); } }
转换流
在学习字符流(FileReader、FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter。
OutputStreamWriter类
public static void writeCN() throws Exception { //创建与文件关联的字节输出流对象 FileOutputStream fos = new FileOutputStream("c:\\cn8.txt"); //创建可以把字符转成字节的转换流对象,并指定编码 OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8"); //调用转换流,把文字写出去,其实是写到转换流的缓冲区中 osw.write("你好");//写入缓冲区。 osw.close(); }
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。
InputStreamReader类
public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //演示字节转字符流的转换流 readCN(); } public static void readCN() throws IOException{ //创建读取文件的字节流对象 InputStream in = new FileInputStream("c:\\cn8.txt"); //创建转换流对象 //InputStreamReader isr = new InputStreamReader(in);这样创建对象,会用本地默认码表读取,将会发生错误解码的错误 InputStreamReader isr = new InputStreamReader(in,"utf-8"); //使用转换流去读字节流中的字节 int ch = 0; while((ch = isr.read())!=-1){ System.out.println((char)ch); } //关闭流 isr.close(); } }
转换流和子类区别
发现有如下继承关系:
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、使用默认编码。
缓冲流
Java中提高了一套缓冲流,它的存在,可提高IO流的读写速度缓冲流,根据流的分类分类字节缓冲流与字符缓冲流。
字节缓冲流
字节缓冲流根据流的方向,共有2个。
写入数据到流中,字节缓冲输出流 BufferedOutputStream。
读取流中的数据,字节缓冲输入流 BufferedInputStream。
它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了IO流的读写速度。
字节缓冲输出流BufferedOutputStream
public class BufferedOutputStreamDemo01 { public static void main(String[] args) throws IOException { //写数据到文件的方法 write(); } /* * 写数据到文件的方法 * 1创建流 * 2写数据 * 3关闭流 */ private static void write() throws IOException { //1创建基本的字节输出流 FileOutputStream fileOut = new FileOutputStream("abc.txt"); //使用高效的流,把基本的流进行封装,实现速度的提升 BufferedOutputStream out = new BufferedOutputStream(fileOut); //2写数据 out.write("hello".getBytes()); //3关闭流 out.close(); } }
字节缓冲输入流 BufferedInputStream
/* * 从文件中读取数据 * 1,创建缓冲流对象 * 2,读数据,打印 * 3,关闭 */ private static void read() throws IOException { //1,创建缓冲流对象 FileInputStream fileIn = new FileInputStream("abc.txt"); //把基本的流包装成高效的流 BufferedInputStream in = new BufferedInputStream(fileIn); //2,读数据 int ch = -1; while ( (ch = in.read()) != -1 ) { //打印 System.out.print((char)ch); } //3,关闭 in.close(); }
基本流与缓冲流复制文件的比较
package com.oracle.demo02; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferDemo { public static void main(String[] args) throws IOException { //write(); read(); } public static void write() throws IOException { long s=System.currentTimeMillis(); //文件字节输出流 FileOutputStream fos=new FileOutputStream("d:\\test\\demo.txt"); //缓冲字节输出流 BufferedOutputStream bos=new BufferedOutputStream(fos); bos.write(100); bos.close(); long e=System.currentTimeMillis(); System.out.println(e-s); } public static void read() throws IOException { long s=System.currentTimeMillis(); //文件字节输入流 FileInputStream fis=new FileInputStream("d:\\test\\demo.txt"); //缓冲字节输入流 BufferedInputStream bos=new BufferedInputStream(fis); int len=0; byte[] b=new byte[1024]; while((len=bos.read(b))!=-1) { System.out.println(new String(b,0,len)); } bos.close(); } }
字符缓冲流
完成文本数据高效的写入与读取操作
字符缓冲输出流 BufferedWriter
/* * BufferedWriter 字符缓冲输出流 * 方法 * public void newLine()写入一个行分隔符 * * 需求: 通过缓冲输出流写入数据到文件 * 分析: * 1,创建流对象 * 2,写数据 * 3,关闭流 * */ public class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建流 //基本字符输出流 FileWriter fileOut = new FileWriter("file.txt"); //把基本的流进行包装 BufferedWriter out = new BufferedWriter(fileOut); //2,写数据 for (int i=0; i<5; i++) { out.write("hello"); out.newLine(); } //3,关闭流 out.close(); } }
字符缓冲输入流 BufferedReader
/* * BufferedReader 字符缓冲输入流 * * 方法: * String readLine() * 需求:从文件中读取数据,并显示数据 */ public class BufferedReaderDemo { public static void main(String[] args) throws IOException { //1,创建流 BufferedReader in = new BufferedReader(new FileReader("file.txt")); //2,读数据 //一次一个字符 //一次一个字符数组 //一次读取文本中一行的字符串内容 String line = null; while( (line = in.readLine()) != null ){ System.out.println(line); } //3,关闭流 in.close(); } }
使用字符缓冲流完成文本文件的复制
/* * 采用高效的字符缓冲流,完成文本文件的赋值 * * 分析: * 1,指定数据源, 是数据源中读数据,采用输入流 * 2,指定目的地,是把数据写入目的地,采用输出流 * 3,读数据 * 4,写数据 * 5,关闭流 */ package com.oracle.demo02; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Copy { public static void main(String[] args) throws IOException { // 数据源 FileReader fr = new FileReader("d:\\test\\a\\demo.txt"); BufferedReader br = new BufferedReader(fr); // 目的地 FileWriter fw = new FileWriter("d:\\test\\demo.txt"); BufferedWriter bw = new BufferedWriter(fw); String len = null; while ((len = br.readLine()) != null) { bw.write(len); bw.flush(); } br.close(); bw.close(); } }