------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ----------
一、字节流File读写操作
1、概述
字节流常用于操作其他媒体文件。
2、由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
3、读写字节流:InputStream 输入流 OutputStream 输出流
4、为何不用进行刷流动作:
字符流其实一样用的字节,但是它需要把字节临时存起来,一个中文两个字节,读一个字节后,不能立刻操作,所以,读完一个字节,再读一个字节,查表去,查完表,才能进行操作,所以需要刷新动作。
而如果直接使用字节流,没有具体缓冲区,就是不管什么数据,我都以字节来操作,读一个字节,操作一次,所以它可以直接把字节写到目录里面,所以它是不需要刷新的。
到了缓冲区的时候,才到了刷新机制。
5、InputStream特有方法:
int available(); 返回文件中的字节个数
可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。
6、三种方式读取数据
1)一个一个读
2)用字节数组缓冲 Byte[]buf=new byte[1024];
3)int available() 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数
哪一种好呢?
思考一个问题:
字节流是操作图片或声音等数据的,现在有一个电影文件,大约有1GB。也就是说available()返回的数值会很大,内存如果还没有1GB,用第三种的话,内存是不就爆了。内存溢出
所以,第三种方法要慎用,如果数据过大,还是以第二种为主。
7、 练习 复制一个图片
8、自定义字节流缓冲区
根据字节流缓冲区的原理,自定义一个字节流缓冲区。
注意:字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会出现数据还没有读完已经结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。
byte类型的-1提升为int类型时还是-1。原因:因为在8个1前面补的全是1导致的。如果在8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&255即可。
练习 MP3文件的复制
练习:自己定义一个字节流的缓冲区来复制MP3文件。
图
二、IO流——读取键盘录入
需求:读取键盘录入。之前我们输入流中都是文件的数据,是硬盘中的数据,现在我们的数据源变了,我们想从键盘获取数据。
1、概述
System.out:对应的是标准的输出设备,屏幕或者控制台
System.in:对应的是标准的输入设备,默认是键盘。
打开System.in, public static final InputStream in ,发现正是字节读取流。
2、关于录入一行数据的问题
需求:通过键盘录入数据
当录入一行数据后,就将该行数据打印。如果录入的数据是over,那么停止录入。
import java.io.*;class Readin{public static void Main(String[]args) throws IOException{InputStream in=system.in;//关键是循环停止的条件是什么呢?只要是键盘录入,只有按ctrl+c才能停止,而ctrl+c就是往程序插入一个结束标记。而且我们是想录入一行数据再打印。StringBuilder sb=new StringBuilder();while(true) //注意一点,当一行已经打印完后,要清空缓冲区。{int ch=in.read();if(ch=='\r')continue;if(ch=='\n){String s=sb.toString();if("over".equals(s))break;System.out.println(s.toUpperCase());sb.delete(0,sb.length());//清空缓冲区的作用}elsesb.append((char)ch);}}}
这个代码似曾相识,因为readline方法就是如此实现的。
问题:
ReadLine方法是字符流BufferedReader类中的方法
而键盘录入的read方法是字节流InputStream的方法
解决方法:转换流
三、转换流
1)概述
public class InputStreamReader extends Reader
InputStreamReader 是字节流通向字符流的桥梁:
它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
2)字节流转换字符流操作步骤
A 获取键盘录入对象
B 将字节流对象转成字符流对象,使用转换流:InputStreamReader
InputStreamReader isr=new InputStreamReader(in);
C为了提高效率,将字符流进行缓冲区技术的高效操作,使用BufferedReader
BufferedReader bufr=new BufferedReader(isr);
故此,键盘录入固定简化格式变为:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
4、练习
3) OutputStreamWriter字符流转换字节流
录入的是字符数据,存到硬盘上的是字节数据。步骤和InputStreamReader转换流一样。
public class OutputStreamWriter extends Writer
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。
常用形式:
Writer out = new BufferedWriter(new OutputStreamWriter(System.out));
四、流操作规律
只要我们明确了我们操作流的源和目的,以及明确所操作数据的性质,是纯文本还是字节数据,是否需要提高效率等,就可以轻而易举完成流操作。
1、源是键盘录入
目的是控制台
2、需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的 文件
使用字节流转换字符流的转换流:InputStreamReader
3、需求:想要将一个文件的数据打印在控制台上
源:文件
目的:控制台
使用字符流转换为字节流的转换流:OutputStreamWriter
4、流操作的基本规律:
通过三个明确来完成
1) 明确源和目的
源: 输入流 InputStream Reader
目的:输出流 OutputStream Writer
2) 明确操作的数据是否是纯文本。
是:字符流
不是 :字节流
3) 当体系明确后,再明确要使用具体哪个对象
通过设备来进行区分
源设备有:内存、硬盘、 键盘
目的设备:内存,硬盘,控制台
5、举例说明
5.1 第一个需求:将一个文本文件中的数据存储到另一个文件。复制文件
1)源:因为是源,所以使用读取流 InputStream Reader
是不是操作文本文件? 是:选择Reader
这样,体系就明确了。 接下来,明确要使用该体系中的哪个对象。
明确设备:硬盘的文件
Reader体系中可以操作文件的FileReader
2) 目的:OutputStream Writer
是否是纯文本? 是 Writer
设备:硬盘的一个文件
Writer体系中可以操作文件的对象是FileWriter
代码
FileReader fr=new FileReader(“a.txt”);
问自己:是否需要提高效率?
是:加入Reader体系中的缓冲区:BufferedReader
BufferedReader bufr=new BufferedReader(fr);
FileWriter fw=new FileWriter(“g.txt”);
问自己:是否需要提高效率?
是:加入Reader体系中的缓冲区:BufferedWriter
BufferedWriter bufw=new BufferedWriter(fw);
5.2 第二个 需求:将键盘录入的的数据保存到一个文件中。
这个需求中,有源和目的都存在
分别分析:
A 源:InputStream Reader
是不是纯文本? 是,Reader
设备:键盘。对应的对象是System.in
不是选择Reader吗?System.in对应的不是字节流吗
为了操作键盘的文本数据方便。可以转成字符流,按照字符串操作是最方便的。
所以,既然明确了Reader,就将System.in转成Reader
用到Reader体系中转换流InputStreamReader
InputStreamReader isr=new InputStreamReader(Sysem.in);
需要提高效率吗?需要 BufferedReader
BufferedReader bufr=new BufferedReader(isr);
B目的 OutputStream Writer
是否是纯文本?是 Writer
设备:硬盘,一个文件。使用FileWriter
FileWriter fw=new FileWriter(“c.txt”);
需要提高效率吗?需要,
BufferedWriter bufw=new BufferedWriter(fw);
5.3第三个需求:想要把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。
目的:OutputStream Writer
是否是纯文本?是,Writer
设备:键盘,一个文件,使用FileWriter
但是,存储时,需要加入指定的编码表,而指定的编码表,只有转换流才可以指定。
所以要使用的对象是OutputStreamWriter
而该转换流对象要接收字节输出流,而且还可以操作的文件的字节输出流,FileOutputStream
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(“c.txt”),”UTF-8”);
需要提高效率吗?需要
BufferedWriter bufw=new BufferedWriter(osw);
总结
所以,记住,转换流什么时候用呢?
字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。
练习
将一个文本数据打印到控制台上,要按照以上格式完成三个明确
1)源:InputStream Reader
是否是纯文本数据?是,Reader
设备:硬盘上的文件。FileReader
是否需要提高效率?是 BufferedReader
BufferedReader bufr=new BufferedReader(new FileReader(filename));
2)目的 OutputStream Writer
是否纯文本数据?否,OutputStream 需要用到转换流 OutputStreamWriter
设备:控制台。System.out
是否需要提高效率?是 BufferedWriter
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
6、改变标准输入输出流
需求:复制文件
class TransStreamDemo{public static void Main(String[]args) throws IOException{//键盘录入BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("out.txt")));//但是目的地要改动,存到文件里BufferedWiter bufw=new BufferedWriter(new InputStreamWriter(System.out));String line=null;while((line=bufr.readLine())!=null){if("over".equals(line))break;//System.out.println(line.toUpperCase());bufw.write(line.toUpperCase());bufw.newLine();//换行bufw.flush();}bufr.close();}}
System中提供一个方法,可以改变标准输入和标准输出
static void setIn(InputStream in)
重新分配“标准”输入流。
static void setOut(PrintStream out)
重新分配“标准”输出流。
这样就可以不用改动,完成复制。
改动目的即可。
7、异常日志信息
实际开发中, 当程序在执行时出现问题并是不希望直接打印给用户看的,图形界面也不会有控制台,而是需要作为文件存储起来,方便程序员查看,并及时调试程序,优化代码的。这就需要异常日志文件。
8、IO流——系统信息存进硬盘文件
void list(PrintStream out)
将属性列表输出到指定的输出流。
void list(PrintWriter out)
将属性列表输出到指定的输出流。
流的基本应用小结:
1)流是用来处理数据的。
2)处理数据时,一定要先明确数据源,与数据目的地(数据汇)。
3)数据源可以是文件,可以是键盘。
4)数据目的地可以是文件、显示器或者其他设备。
5)而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。