借用下图:
使用IO操作文件时的一般思路如下:
- 确定源
- 确定流,选择哪种流
- 操作:读/写(一个一个的读/写,还是一段一段的读/写)
- 释放系统资源
使用IO流的时候,只能操作文件,是不能操作文件夹的。
逐个字节读取数据
从JDK1.8的API中发现:
这是InputStream类的部分方法,可见read()方法是抽象方法,必须由子类继承并实现这个抽象方法。
这是FIleInputStream类的部分方法,创建文件字节输入流必须是父类的引用指向子类的对象,子类必须实现父类的抽象方法。
程序实例:
package IO_Study;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class IOstudy01 {
public static void main(String[] args) {
//1. 创建源
File src = new File("E:/(A)PostgraduateFlies/JavaLearning/
JavaProjects/TestingProjects/src/IO_Study/abc.txt");
try {
//2.选择流
InputStream is = new FileInputStream(src);
//3.操作(读取)
int data1 = is.read();//读取第一个字符
int data2 = is.read();//读取第二个字符
int data3 = is.read();//读取第三个字符
int data4 = is.read();//读取第四个字符
System.out.println((char) data1);
System.out.println((char) data2);
System.out.println((char) data3);
System.out.println(data4);//文件中只有三个字符,所以读取不到第四个字符,文档末尾返回-1
//4.通知虚拟机释放系统资源,但是虚拟机做不做不一定
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
文件abc.txt 里面存储的内容是“abc”,使用UTF-8编码方式时,只有三个字节,所以每次读取一个字节(就是一个字母),但是文件没有第四个字节,读取时文件末尾会返回“-1”.
程序改进:
package IO_Study;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 1.确定源
* 2.选择流
* 3.操作
* 4.释放系统资源
*/
public class IOstudy02 {
public static void main(String[] args) {
//确定源
File src = new File("E:/(A)PostgraduateFlies/JavaLearning/
JavaProjects/TestingProjects/src/IO_Study/abc.txt");
InputStream is = null;
try {
//选择流
is = new FileInputStream(src);
//操作(读取文件里面的数据)
int temp;
while ((temp = is.read()) != -1) {
//文件末尾是-1
System.out.println((char) temp);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放
try {
if (null != is) {
//有可能文件字节流建立失败,所以要判断
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
需要说明的是:
- 新建File对象有可能是空对象,不指向具体的文件,所以要捕获异常;
- 这里使用FileInputStream类,文件字节输入流来读取文件里的数据,为了保证读取正确,所以被读取的文件里面都是按照UTF-8编码方式占一位字节的字母和数字,工程默认的编码方式也是UTF-8,显示时不会乱码;
- 对于异常捕获语句try…catch…finally…,无论有误异常,finally语句都是最后执行,且一定执行;相关知识点参考第一阶段的异常章节;
- 因为不确定是否成功建立数据流,所以在执行释放系统资源的时候需要先判断是否已经成功建立数据流。
通过使用FileInputStream类的read()方法,我发现一个问题,描述:以上程序是连续使用四个read()语句,每次读取一个字节的数据,问题是read()语句怎么知道我想读取第几个数据?这里没有指针也没有标识变量,如果是根据read()语句的相对位置来确定读取到第几个字节数据了,假设有一个现实需求,文件里面有一百个字节数据,每读取十个数据程序会进行其他操作,那继续读取的时候,如何保证是从上次读取位置的下一个开始?
如果文件比较多,可以使用一段一段的读取的方式进行读取。
读到缓冲缓存里面,
分段读取字节数据
这是FileInputStream类的分段读取的read()方法:
需要注意的是,逐个字节读取的返回值类型为int,返回的是单个字节数据的ASCII码,是整型数据,分段读取字节数据的返回值仍然int类型,但是返回值是一共读取到字节的数据的个数。
package IO_Study;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 1.确定源
* 2.选择流
* 3.操作
* 4.释放系统资源
*/
public class IOstudy03 {
public static void main(String[] args) {
//确定源
File src = new File("E:/(A)PostgraduateFlies/JavaLearning
/JavaProjects/TestingProjects/src/IO_Study/abc.txt");
InputStream is =null;
try {
//选择流
is = new FileInputStream(src);
//操作:分段读取
byte[] car = new byte[3];
int car1 = is.read(car);
//字节数组-->到字符串(解码)
String str = new String(car, 0,car1);//参考String的有参构造器
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭流
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
abc.txt文件里面存的是abcde,读取正确。
复习一下String类的其中构造方法:
public String(byte bytes[], int offset, int length) { //从bytes[]的下标offset开始,长度为length,解码,字节数组 //转换成字符串 checkBounds(bytes, offset, length); this.value = StringCoding.decode(bytes, offset, length); }
package IO_Study;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 1.确定源
* 2.选择流
* 3.操作
* 4.释放系统资源
*/
public class IOstudy03 {
public static void main(String[] args) {
//确定源
File src = new File("E:/(A)PostgraduateFlies/JavaLearning/JavaProjects/TestingProjects/src/IO_Study/abc.txt");
InputStream is = null;
try {
//选择流
is = new FileInputStream(src);
//操作:分段读取
byte[] flush = new byte[3];//缓冲容器
int length = -1;
while ((length = is.read(flush)) != -1) {
//读取到缓冲容器flush(字节数组)中
String str = new String(flush, 0, length);//解码,字节数组转换成字符串
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭流
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
在实际生产环境下,读取数据是按照KB为单位进行读取,通常情况下会读取几十KB,如下:
byte[] flush = new byte[1024*50];//缓冲容器
这里的flush是缓冲容器,这里是每次读取1024字节*50=50KB。
文件字节输出流
FileOutputStream类的使用方法和步骤与FileInputStream类相似:
- 确定源
- 确定流,选择哪种流
- 操作:读/写(一个一个的读/写,还是一段一段的读/写)
- 刷新flush,防止数据滞留在内存中;
- 释放系统资源
package IO_Study;
import java.io.*;
/**
* 测试FileOutputStream文件字节输出流
* 1.确定源
* 2.选择流
* 3.操作
* 4.释放系统资源
*/
public class IOstudy04 {
public static void main(String[] args) {
//1.确定源
File dest = new File("dest.txt");
//2.选择流
OutputStream os = null;
try {
//2.选择流
os = new FileOutputStream(dest);
//3.操作,写数据
String msg = "fanfada";//需要写的数据
byte[] datas = msg.getBytes();//getBytes():字符串-->字节数组,编码
os.write(datas);
os.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != os) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
借助这个程序,复习一些知识点:
-
使用文件字节输出流FileOutputStream通过字节的方式写出或者追加到文件,就必须把需要写的内容转换成字节(数组);
-
String msg = "fanfada";//需要写的数据 byte[] datas = msg.getBytes();//getBytes():字符串-->字节数组,编码 getBytes()方法://Encodes this {@code String} into a sequence of bytes, public byte[] getBytes() { return StringCoding.encode(value, 0, value.length); } 返回值是字节数组,也就是字符串-->字节数组,即:编码!
-
os = new FileOutputStream(dest, false); //3.操作,写数据 String msg = "fanfada";//需要写的数据 byte[] datas = msg.getBytes();//getBytes():字符串-->字节数组,编码 os.write(datas);//创建流的append标志如果为true,这里的write方法就是追加,如果为false,就是覆盖写入 os.flush();
-
每次执行write()方法的之后建议flush一次,避免数据滞留在内存中,另外close()方法在关闭之前也会自动flush,所以说最后一次写入的时候不用flush。