文章目录
字节输入流 InputStream
字节输出流 OutputStream
文件字节输入流 FileInputStream
构造方法:
1 FileInputStream(File file) 通过指定的文件建立输入流通道
FileInputStream fis=new FileInputStream(new File("1.txt"));
2 FileInputStream(String name) 通过字符串的路径建立输入流通道。
FileInputStream fis=new FileInputStream("1.txt");
注意:如果文件找不到,报异常 FileNotFoundException构造方法只能封装文件路径,封装文件夹路径是没意义的,因为他本来就是空的,不过可以遍历文件夹,在分别读文件。
常用方法
1.read() 从输入流中读取一个字节。
如果达到文件的末尾,返回-1.(返回值是byte数据转成的int。如:文件的a返回97)
测试数据,在项目下有一个 1.txt 文件,里面保存abc
FileInputStream fis=new FileInputStream(new File("1.txt"));
int result =0;
while((result=fis.read())!=-1) {
System.out.print(result);
System.out.println((char)result);
}
fis.close();
结果:
97a
98b
99c
2.read(byte[] b)将 最多b.length个数据读取到字节数组中.
注意: 返回值是读取到的字节个数。读取到的数据放在了字节数组中。 如果达到文件的末尾,返回-1
理解:java中他的源码是调用的native修饰的 方法,看不见是怎么实现的。大概应该是用的c语言实现的。read(byte[] b)获取每个字节放进byte数组里,最多放数组的长度个。如数组是new byte[1024],就可以放1024个字节
使用:
public void static void main(String[] args)throws IOException{
FileInputStream fis=new FileInputStream(new File("1.txt"));
//定义一个用来存储数据的byte数组
byte[] bytes = new byte[1024];//数组的长度可以是任意值,通常情况下为1024。或1024的倍数
//定义一个int类型,接受返回值,即读取到的个数,给他默认值0
int readNum = 0;
while ((readNum = fis.read(bytes)) != -1){
//把每个byte数组转为字符串
System.out.println(new String(bytes,0,readNum));
}
fis.close();
}
代码解释:
理解:使用方法中,会用while循环,循环往数组里放,那么会有三种情况,1,数组没满,但是文件读完了。 那么直接返回读到个字节数。再走继续read就是-1,退出循环。2,数组满了,文件刚好读完了。然后又一次while循环,返回-1,退出循环。3,数组满了,但是文件没有读完。就再循环一次,从数组中挨个覆盖,如果第二次到数组存到一半,文件读完了,就会返回这次读到的个数,注意,这时数组上半部分是最后一次读的,后半部分是上一次读的后部分,就是第二次循环的时候他不会清空数组,而是挨个覆盖。然后再次循环返回-1,退出循环。
细节:通过 new String(bytes,offset,count) 构造方法,转换成字符串
其中 ,bytes表示数组,offset偏移量表示从哪个位置开始进行转换,count表示读取的个数。
为什么要用这个String(bytes,offset,count)
构造方法? 因为byte数组中没有存放读取字节的,里边都是0,如果一个文件只有3个字节,但是数组长度是1024,那么直接输出数组,他会有1021个没用的0,只有3个 是我们想要的。(在下一个构造方法中会体现出来)所以用String(bytes,offset,count) 只获取读取到的长度,解决上述问题。
3 read(byte[] b, int off, int len) 从数组off索引位置开始放入,读取的长度为len 返回值是读取的个数。
实例
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("1.txt");
byte[] b=new byte[10];
int value = fis.read(b, 3, 3);
System.out.println(value);
System.out.println(Arrays.toString(b));
System.out.println(new String(b,3,value));
fis.close();
}
输出结果:
3
[0, 0, 0, 97, 98, 99, 0, 0, 0, 0]
abc
注意
从返回结果可以看出来,数组里没有存放读取的字节的都是0.
4 close() 关闭资源,每次使用完成都要关闭资源
5 available() 返回读取的文件的剩余字节数,如果没有调用read方法,就是文件的总字节数。
文件字节输出流 FileOutputStream
构造方法
1 FileOutputStream(File file) /2 FileOutputStream(String name) 创建一个输出流。该输出流会自动创建一个新的文件或者覆盖原文件。
3 FileOutputStream(File file, boolean append) /4 FileOutputStream(String name, boolean append)
创建一个输出流,该输出流当第二个参数的值为true的时候,从文件末尾插入,不覆盖。第二个参数值为false 时与没有append参数一样,直接覆盖。
常用方法
① write(int b) 将一个字节数据写入到文件中。
int b 指字节对应的ASCII值,这个时候,会将int类型转成byte类型存储。
byte转int在这个帖子有介绍
② write(byte[] b)将一个数组的字节写入到文件中。
实例
String类型转byte数组。str.getBytes();
public static void main(String[] args)throws IOException{
FileOutputStream fos=new FileOutputStream("1.txt");
fos.write("随便写一个String转成byte数组".getBytes());
fos.close();
}
③ write(byte[] b, int off, int len) 从指定的off索引位置开始写入,写入len个数组的数据。
在复制文件的时候用。看复制图片例子
④ close() 关闭资源
高效读取的字节流
缓冲区的字节流 : 自身内部包含缓冲区,即数组,该数组长度为8192个。用法跟文件字节流基本一样,只是比较高效。
文件字节缓冲输入流 BufferedInputStream
构造方法
BufferedInputStream(InputStream in)
常用方法
read(byte[] )
文件字节缓冲输出流 BufferedOutputStream
构造方法
BufferedOutputStream(OutputStream out)
常用方法
write(byte[] ,off,len)
注意,缓冲流多一个flush()
方法;flush是将缓冲区的数据刷新到文件中。close()是关闭流,这个流就不能使用了,但是close之前会调用flush方法。
用法(复制图片):
这个是缓冲字节流实现的,用字节流同理。(注意:write必须用write(bytes,0,readNum)这个方法,不然最后一次可能会多出来一段倒数第二次读到的数据,也可能多出来空的。因为read(byte[] b)的特点,在上边有)
public class BufferedInputStudy {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\logo.png")));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File("C:\\Users\\Administrator\\Desktop\\logo-copy.png")));
byte[] bytes = new byte[1024];
int readNum = 0;
while ((readNum = bis.read(bytes)) != -1) {
bos.write(bytes,0,readNum);
}
//输出流必须关,不然输出不全,或者不输出??
bis.close();
bos.close();
}
流的异常处理形式
jdk 1.6 以复制文件为例
格式:
try{
.....
}catch(){
处理异常 ....
} finally{
关闭流....
}
用法:
public static void main(String[] args) {
//提升作用域
FileInputStream fis =null;
FileOutputStream fos=null;
try {
int i=5/0;//这个时候抛出除零异常,流是null,关闭流会报空指针,所以关闭资源的时候需要判断
fis = new FileInputStream(new File("1.txt"));
fos = new FileOutputStream(new File("3.txt"));
int readNum=0;
byte[] b=new byte[1024];
while((readNum=fis.read(b))!=-1) {
fos.write(b,0,readNum);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 关闭资源。
try {
//判断流是否为null 不为空才可以关闭(重要)
if (fis!=null) {
fis.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//判断流是否为null 不为空才可以关闭(重要)
if (fos!=null) {
fos.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一定要记得关闭流的时候,判断是否为空
jdk 1.7
try-with-resources格式
用法:
public static void main(String[] args) {
try(
FileInputStream fis=new FileInputStream(new File("1.txt"));
FileOutputStream fos=new FileOutputStream(new File("3.txt"));
){
int value=0;
byte[] b=new byte[1024];
while((value=fis.read(b))!=-1) {
fos.write(b,0,value);
}
}catch (Exception e) {
e.printStackTrace();
}
}
注意
创建字节输入流( FileInputStream)的时候,路径找不到会抛异常,创建字节输出流(FileOutputStream)的时候,路径中的文件或目录没有就创建,有一样的就覆盖。
读写效率:用byte[]的读写方法比直接read()和write()效率高。
缓冲字节流改进了字节流,比一般字节流效率高
递归实现复制目录
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 作者: 帅的一批 时间:2019年12月12日下午4:12:45 学习: 内容: 复制一个文件夹到另一个文件夹
*/
public class CopyFiles {
public static void main(String[] args) throws IOException {
File f1 = new File("E:\\File");
File f2 = new File("E:\\File1");
copyFiles2(f1, f2);
}
//单纯的复制文件
public static void copyFile2(File f1, File f2) throws IOException {
FileInputStream fis1 = new FileInputStream(f1);
FileOutputStream fos2 = new FileOutputStream(f2);
int readNum = 0;
byte[] bytes = new byte[1024];
while ((readNum = fis1.read(bytes)) != -1) {
fos2.write(bytes, 0, readNum);
}
System.out.println("复制文件" + f1.getAbsolutePath() + "到" + f2.getAbsolutePath() + "成功。");
}
/**
*
* @param f1 把f1复制一份给f2
* @param f2 把f1复制一份给f2
* @throws FileNotFoundException
* @throws IOException
*/
public static void copyFiles2(File f1, File f2) throws FileNotFoundException, IOException {
if (f1.getAbsolutePath().equals(f2.getAbsolutePath())) {
System.out.println("路径名一样,再见来不及握手。");
return;
} else if (!f1.exists()) {
System.out.println("文件或目录不存在,再见来不及握手");
return;
} else if (f2.getName().contains(".")) {
System.out.println("复制到的目录名格式错误,不要包含 '.' ");
return;
}
// 如果都是文件就复制
if (f1.isFile()) {
System.out.println("创建" + f2.getAbsolutePath() + "目录:" + f2.mkdir());
File newF2 = new File(f2.getAbsolutePath() + "//" + f1.getName());
copyFile2(f1, newF2);
}
// 如果都是目录,就遍历copy
if (f1.isDirectory()) {
File[] files = f1.listFiles();
if (files != null) {
for (File file : files) {
// 创建新文件路径,这个文件或文件夹与父目录一一对应。
File newFile = new File(f2.getAbsolutePath() + "\\" + file.getName());
// 如果是文件夹就创建文件夹,如果是文件就创建文件;如果是文件夹,就继续遍历,一直找到文件为止;找到文件就复制
if (file.isDirectory()) {
// 目录 创建目录,修改流,再递归
// File newFile = new File(f2.getAbsolutePath() + "\\" + file.getName());
System.out.println("创建" + newFile.getAbsolutePath() + "文件夹:" + newFile.mkdirs());
copyFiles2(file, newFile);
} else {
// File newFile = new File(f2.getAbsolutePath() + "\\" + file.getName());
copyFile2(file, newFile);
}
}
}
}
}
}