Java基础22-Buffered缓冲IO流
Buffered缓冲IO流
缓冲IO流:
是处理流,负责在其他IO流基础上增加缓冲功能。提高效率
BufferedReader —> Reader
BufferedWriter —> Writer
BufferedInputStream —> InputStream
BufferedOutputStream —> OutputStream
BufferedReader除了继承了Reader的那些读的方法,还增加了一个:`String readLine()`读取一行(读取一个文本行)BufferedWriter除了继承了Writer的那些写的方法,还增加了一个:`void newLine()`写换行符默认的缓冲区的大小:8192字节/字符
@Testpublic void test01() throws IOException{
//用它来读取一个文件,因为控制台只能显示文本数据,所以我们这里用纯文本文件来演示FileReader fr = new FileReader("1.txt");BufferedReader br = new BufferedReader(fr);//数据:1.txt --> fr --> brString str ;while((str = br.readLine()) != null){
System.out.println(str);}br.close();fr.close();}
用缓冲流和没用缓冲流的区别:
@Testpublic void test02()throws IOException{
long start = System.currentTimeMillis();FileReader fr = new FileReader("1.txt");BufferedReader br = new BufferedReader(fr);FileWriter fw = new FileWriter("11.txt");BufferedWriter bw = new BufferedWriter(fw);String str ;数据:1.txt --> fr --> br --> str --> bw --> fw -- >11.txtwhile((str = br.readLine()) != null){
bw.write(str);bw.newLine();//换行}bw.close();fw.close();br.close();fr.close();long end = System.currentTimeMillis();System.out.println("毫秒:" + (end-start));}
不带缓冲流的
public class TestTxtCopy {
@Testpublic void test01() throws IOException{
long start = System.currentTimeMillis();copy("1.txt", "12.txt");long end = System.currentTimeMillis();System.out.println("毫秒:" + (end-start));}/** 纯文本文件的复制*/public void copy(String srcFileName, String destFileName) throws IOException{
//1、选择IO流,并创建IO流FileReader fr = new FileReader(srcFileName);FileWriter fw = new FileWriter(destFileName);//2、一边读一边写char[] arr = new char[10];int len;//数据从 srcFileName文件 --》 fr --> arr数组 --> fw --> destFileNamewhile((len = fr.read(arr)) != -1){
fw.write(arr, 0, len);}//3、关闭fw.close();fr.close();}
}
可以看出有缓冲流速度更快,原因:
有缓冲流,先将数据都暂存到缓冲流里。达到一定数量后再一次性写进或者读出,不用每读一次或者写一次就马上读写,更节约时间
默认字节流对应的缓冲流大小为8192字节,字符流对应的为8192字符,也可以自己设置大小
8192=1024*8=8K
复制任意类型文件示例:
@Testpublic void test03() throws IOException{
long start = System.currentTimeMillis();copy("D:/software/IDEA/ideaIU-Ultimate-2017.1.4.exe","d:/idea.exe");long end = System.currentTimeMillis();System.out.println("毫秒:" + (end-start));}//复制任意类型的文件的功能//任意类型的文件,只能选择字节流public void copy(String srcFilename , String destFilename) throws IOException{
FileInputStream fis = new FileInputStream(srcFilename);BufferedInputStream bis = new BufferedInputStream(fis);//fis比喻成内衣,bis是外套FileOutputStream fos = new FileOutputStream(destFilename);BufferedOutputStream bos = new BufferedOutputStream(fos);byte[] arr = new byte[10];int len;//数据: srcFilename --> fis --> arr --> fos --> destFilenamewhile((len = bis.read(arr)) !=-1){
bos.write(arr, 0, len);}bis.close();//先脱外套fis.close();//再脱内衣bos.close();fos.close();}
注意关闭流的顺序,当有多个流套起来时,比如以上例子,bos套的fos,bis套的fis,一种方法只关外层的,即只关bos和bis,它会自动将内层的fos和fis关闭,另一种都关,但要注意顺序,先关外层,再关内层,若是顺序错了运行会报错,数据可能会少,因为顺序错误输出流关闭异常,输出流缓冲区里还有数据没处理,当正常关闭时,会自动刷一遍缓冲区,因为既没正常关闭流,又没自己刷一遍缓冲区,所以有数据缺失fos和fis、bos和bis之间没有先后关系,因为没有依赖关系
关于换行符的说明
@Testpublic void test02()throws IOException{
long start = System.currentTimeMillis();FileReader fr = new FileReader("1.txt");BufferedReader br = new BufferedReader(fr);FileWriter fw = new FileWriter("11.txt");BufferedWriter bw = new BufferedWriter(fw);String str ;数据:1.txt --> fr --> br --> str --> bw --> fw -- >11.txtwhile((str = br.readLine()) != null){
bw.write(str);//bw.newLine();//换行}bw.close();fw.close();br.close();fr.close();long end = System.currentTimeMillis();System.out.println("毫秒:" + (end-start));}
运行完数据都在一行,因为br.redLine()按行读的时候,bufferedReader读取数据时将换行符当作数据的分隔符了,没把它当作数据的一部分,所以读取的时候没有取换行符,因此写的时候str里是不包含换行符的,就没有换行,下次写就挨在一起
要换行有两种:
1:自己加\n
即 bw.write(str+"\n");
2:每写完一次加一个newLine()
bw.newLine();//换行, 如例子里的注释
换行符的问题
@Testpublic void test05()throws IOException{
FileWriter fw = new FileWriter("22.txt");fw.write("hello");fw.write("\n");fw.write("world");fw.close();}
直接项目里打开换行了
但是若是文件用记事本打开会发现没有换行
原因:记事本是c语言,window自带的,在c语言里是 \r\n 两个构成换行符,所以一般为了跨平台,一般用newLine()换行,因为直接用\n大部分软件可能支持换行,但c支持的软件不能换行,要么就fw.write("\r\n");//\r本行结束,\n把光标移动到下一行,是两个动作
@Testpublic void test05()throws IOException{
FileWriter fw = new FileWriter("22.txt");fw.write("hello");
// fw.write("\n");fw.write("\r\n");//\r本行结束,\n把光标移动到下一行fw.write("world");fw.close();}
转换流:InputStreamReader
解码: 字节输入流转字符输入流的转换流,同 时还能指定编码方式
InputStreamReader:
案例:
文件编码为ANSI,就是操作系统当前平台的编码
操作系统中文:默认GBK或者GB2312,而eclipse控制台(或者工作空间)的编码为utf-8
读取文件:
@Testpublic void test01() throws IOException{
//乱码:d:/io.txt的编码是GBK,平台是UTF-8FileReader fr = new FileReader("d:/io.txt");char[] arr = new char[1024];int len = fr.read(arr);System.out.println(new String(arr,0,len));fr.close();}
会发现乱码,原因:d:/io.txt的编码是GBK,平台是UTF-8
处理方法:对应的编码解码
@Testpublic void test02() throws IOException{
//String:解码//解码:字节-->字符FileInputStream fis = new FileInputStream("d:/io.txt");byte[] arr = new byte[10];int len = fis.read(arr);//String(byte[] bytes, String charsetName)//String(byte[] bytes, int offset, int length, String charsetName)System.out.println(new String(arr,0,len,"GBK"));fis.close();}
没有乱码
对于GBK来说,一个汉字两个字节,若是将byte数组长度设置为9,则最后一个汉字乱码,因为缺少一个字节,当文件过大或者中英文结合时,不好确认素组长度另一种解码方法,解码IO流:InputStreamReader
解码: 字节输入流转字符输入流的转换流,同时还能指定编码方式
@Testpublic void test03()throws IOException{
//解码:字节-->字符FileInputStream fis = new FileInputStream("d:/io.txt");InputStreamReader isr = new InputStreamReader(fis,"GBK");//将fis流中的字节流按照GBK进行解码为字符流//数据:d:/io.txt(GBK)-->fis(纯字节)-->isr(GBK)解成字符流-->按字符读取char[] arr = new char[3];//字符数组int len = isr.read(arr);System.out.println(new String(arr,0,len));isr.close();fis.close();/注意关闭顺序}
转换流: OutputStreamWriter
编码:字符–》字节
把字符流转为字节流的转换流
OutputStreamWriter:
把字符流转为字节流,并且可以指定编码
@Testpublic void test01()throws IOException{
//当前平台是UTF-8,文件是GBKString str = "柴老师永远18岁!";FileWriter fw = new FileWriter("d:/io.txt");fw.write(str);fw.close();}
写入乱码
解决方法1:用String的编码
@Testpublic void test02()throws IOException{
//当前平台是UTF-8,文件是GBK//String:编码的方法 getBytes()//编码:字符--》字节String str = "柴老师永远18岁!";FileOutputStream fos = new FileOutputStream("d:/io.txt");fos.write(str.getBytes("GBK"));//无参是平台默认的fos.close();}
用转换流
@Testpublic void test03()throws IOException{
//当前平台是UTF-8,文件是GBK//String:编码的方法 getBytes()//编码:字符--》字节String str = "柴老师永远18岁!永远美丽";FileOutputStream fos = new FileOutputStream("d:/io.txt");OutputStreamWriter oos = new OutputStreamWriter(fos, "GBK");oos.write(str);//数据: str(字符)-->oos(字符)按照GBK编码为字节流-->fos --> io.txtoos.close();fos.close();}
转换流: OutputStreamWriter和InputStreamReader只能用于处理纯文本数据的编码和解码
数据IO流:DataInputStream和DataOutputStream
DataOutputStream
程序中有这样一组数据:
int num = 10;
char c = ‘好’;
double d = 88.88;
String info = “尚硅谷真好!”;
boolean good = true;
程序运行过程中,想要临时退出,下次希望从这个状态继续恢复执行。
希望Java能够输出各种数据类型的数据,读取时,能还原各种数据类型的数据。
因为这些数据不是纯文本,那么只能选择字节流。
@Testpublic void test01()throws IOException{
int num = 10;char c = '好';double d = 188.88;String info = "尚硅谷真好!";boolean good = true;FileOutputStream fos = new FileOutputStream("data.dat");//字节流,按字节输出/*fos.write(num);//错误的fos.write(c);fos.write((byte)d);//转换为byte数据会丢失,不转换报错*/DataOutputStream dos = new DataOutputStream(fos);dos.writeInt(num);dos.writeChar(c);dos.writeDouble(d);dos.writeUTF(info);//写字符串,按utf-8的方式来写字符串dos.writeBoolean(good);dos.close();fos.close();}
因为字符串是按utf-8得格式写得,所以能正常识别、看懂,其他的writeInt等按指定的编码方式写进去,utf-8解不了,这些数据只能java程序能解,外面得程序打开是乱码的,除非用另一个java程序去读取,所以java程序里的文件一般不取名.txt,一般这种文件取名.dat,然后用它对应的软件打开(这里即java程序),解决方法就是用DataInputStream读
DataInputStream:用.bat文件存进去,DataInputStream去读取
@Testpublic void test02()throws IOException{
FileInputStream fis = new FileInputStream("data.dat");DataInputStream dis = new DataInputStream(fis);int num = dis.readInt();char c = dis.readChar();double d = dis.readDouble();String s = dis.readUTF();boolean b = dis.readBoolean();System.out.println(num);System.out.println(c);System.out.println(d);System.out.println(s);System.out.println(b);dis.close();fis.close();}
数据类型不同,字节不同,只用FileOutputStream不知道按几个字节去解析,用DataInputStream,读的时候文件不存在运行会报错,写的时候文件不存在会创建一个文件
Java中IO流的类的体系设计,隐含了一个设计模式:装饰者设计模式
DataOutputStream在OutputStream的基础上,增加了很多方法:
writeXxx(…)
DataInputStream在InputStream的基础尚自,增加了很多方法:
Xxx readXxx()
要求:
用DataOutputStream写的文件或数据,得用DataInputStream来读取。
并且要求读的顺序与写的顺序要一致。比如写的时候先写得int在char,读的时候顺序也得一样,不然读出来数据不对