一、什么是字节流
电脑中存储的文件都是以二进制以字节为单位的形式保存,在传输时也是按字节传输。所以说,字节流可以读取以及传输任意格式的文件数据。
注意:在创建字节流时,每创建一个字节流对象都要在最后关闭流。如果操作比较频繁并且不对流进行关闭,很容易就出现输入输出流超越JVM的边界,所以有时候就可能无法回收资源。而且不关闭流时,如果在操作系统里对这个文件进行删除等操作时就会报错,显示该文件正在被某个进程占用。
流关闭原则:先开后关,后开先关。
二、字节输出流
目的:向文件中写入数据
1. 创建一个字节输出流对象
FileOutputStream fw = new FileOutputStream("text.txt");
2. 写入数据
字节流写入数据时操纵的是ASCII码,即代码层面写的数据为所要写入字符的ASCII码,例如写入字符A时就要write(65).
(1)以单个字节形式写入
fw.write(65);//数字代表的ASCII码,写入的是ASCII码对应的字符
fw.write(66);
fw.write(67);
fw.write(68);
fw.write(69);
(2)以字节数组形式写入
byte[] by = {97,98,99,100,101};
fw.write(by);
如果觉得ASCII码记不住可以利用getBytes()方法,将字符内容自动转换为ASCII码
byte[] bys = "12345678".getBytes();//直接将字符转换为字体默认的编码格式
fw.write(bys);
(3)写入字节数组中指定范围的内容
byte[] by = {97,98,99,100,101};
fw.write(by, 1, 3);//写入字节数组中指定范围的内容
3. 换行
我们上面的方法在写入数据时都是一行一行的写满才会换下一行,那么当我们需要主动换行时该怎么办呢?接下来就让我们一起看看。
现在我们已经知道字节流写入字符时都是按其ASCII码写入,那么相应的,换行符‘\n’也有对应的ASCII码,所以我们只要在写入时传入换行符的ASCII码就能实现换行操作。
fw.write("12345\n6789".getBytes());
在这里要注意一点,不用的设备系统得换行符标识不同
windows:\r或\n Linux:\n Mac:\r
4. 追加写入
以上的写入方法都是在文件中从头写入,会覆盖原有的文件内容,那么如果我们只想在原有内容后追加内容怎么办呢?
这是就要回到我们创建输出流对象的时候了,系统默认的创建是不追加,直接从头开始写数据,我们只需要在创建对象时加入参数true,就可以实现追加写入了。
FileOutputStream fw1 = new FileOutputStream("text.txt",true);
fw1.write("\nHolle World".getBytes());
fw1.close();
三、字节输入流
1. 创建一个字节输入流对象
FileInputStream fr = new FileInputStream("text.txt");
2. 读取数据
原数据内容为“ABC”
System.out.println(fr.read());
通过输出可以看到读取的为对应的ASCII,那么怎么让其输出字符呢?我们可以利用char强制转换。
int b =fr.read();
System.out.println((char)b+":"+b);//输出字符及对应的ASCII码,一次只能读一个字符
现在已经能够读取并且输出字符了,可还有一个问题,write方法一次只能读取一个字符,要怎么才能一次读取全部字符呢?我们可以利用循环来反复多次读取,但是读取停止的条件又是什么呢?让我们多读取几个字符看看。
int b =fr.read();
System.out.println((char)b+":"+b);//输出字符及对应的ASCII码,一次只能读一个字符
//多读取几次数据,发现数据读完时返回结果为-1
b =fr.read();
System.out.println((char)b+":"+b);
b =fr.read();
System.out.println((char)b+":"+b);
b =fr.read();
System.out.println((char)b+":"+b);
通过输出结果可以看到当所有内容都被读取完时返回的ASCII码为-1 ,这样我们就有了循环停止的条件,即read返回的值为-1时就停止循环。
int by;
while((by = fr.read()) != -1) {
System.out.print((char)by);
}
四、利用字节输入输出流复制一个文件
要求:将一个文件中的内容复制到另一个文件
思路:每读一次数据就写一次,然后再读下一次,直到读完
代码:
FileInputStream fr = new FileInputStream("text.txt");
FileOutputStream fr1 = new FileOutputStream("text1.txt");
int by;
while((by = fr.read()) != -1) {
System.out.print((char)by);
fr1.write(by);
}
fr1.close();
fr.close();
控制台输出:
原文件与复制文件:
五、以字节数组形式读取
上面我们读取内容时都是以单个字符的形式读取,即对于每个字符都要开关一次读操作,除此之外还可以以字符数组的形式读取,即对于一定数量的字符进行一次读取操作。就好比有一队人要通一扇门,一般的读取就是每个人通过时都要开一次门再关上,而以数组形式读取就是每开一次门后通过一定数量的人之后再关门等待下一波人通过。这样可以大大的减少读写的速度。
下面我们做了一次实验,利用两种不同的读取方法对一个220M大小的文件读取,比较其花费的时间。
以字节数组形式读取:
以单个字节形式读取:
通过上面的实验我们可以看到,以字节数组形式读取文件花费的时间为514ms,大约为0.5秒,而以单个字节形式读取文件花费的时间为530332ms,大约为8.84分钟,后者花费时间为前者的1000多倍,可见差距之大。