Java – IO流 – FileReader字符输入流
简介
操作本地文件的字符输入流,可以把本地文件中的文本数据读取到程序中来。
创建输入流
1.创建字符输入流对象
2.读数据
3.释放资源
创建对象
// 以字符串形式表达文件目录位置
public FileReader(String name);
// 以File 对象的形式表达文件目录位置
public FileReader(File name);
// JDK11 之后新增的数据转换流方式,允许通过自定义字符集的方式解码文件
public FileReader(File name, Charset charName);
注意:如果文件不存在或路径不正确,会直接报错异常。
读取数据
// 读取一个字符的数据,返回 int 型,以ASCII码转出,每调用一次就读取一个字符
// ,如果已经读到文件尾,会返回 -1
public int read();
// 一次读取多个字符的数据,返回 int 型,返回的数据为读取的数据长度,
// 读取的多个字符数据会存到一个字符数组中,一次读取多少字符,全看这个 char[] 数组声明
// 了多少个成员
// 每一次的read 读取中,都会尽可能的覆盖填满 char[] 字节数组,但如果到了文件
// 末尾时,数据存不满,那么上一次填充的数据中,有可能依然还在,因此要注意最后
// 一次读取时的数据处理。
public int read(char[] buffc);
注意:FileReader 的 read 和 FileInputStream 有所不同,FileReader 的 read 当遇到中文字时,会把整个中文字读取,而FileInputStream 的 read 依然只会读取一个字节。
FileReader循环读取
FileReader fr = new FileReader("Learn\\src\\a.txt");
int c;
// 循环读取一个字,不管它是占1字节还是2字节,会自动读取
while ((c = fr.read()) != -1) {
System.out.println((char) c);
}
释放资源
Java在读文件的时候,会对文件进行占用锁定,期间其它程序无法对它进行操作,因此我们对文件操作完成后,应该要对文件进行解锁去除占用。
fr.close();
异常中的处理(普通方式)
FileReader
中,包含了编译时异常,如果调用FileReader
创建构造时,会出现编译错误,需要我们 throws
抛出异常。
同时也可以使用 try...catch
的方式进行处理。
但是 try...catch
中存在两个问题:
问题一: FileReader
找不到文件时会直接抛出异常,try
中一旦出现异常时,会停止执行 try
下面的代码,这使得 close()
关闭流这种方法有可能没有执行到。
try {
// 如果 FileReader 出现异常,后面的代码不会执行
FileReader fr = new FileReader ("不存在的文件");
// 这里的代码不会被执行,所以 fr.close() 也不会被执行
fr.close();
}catch (IOException e){
e.printStackTrace();
}finally {
// 如果把 fr.close() 放在这里,则因为作用域的原因,会找不到 fr 对象
fr.close();
}
解决方法是,把 FileReader
创建对象移出 try
作用域外
// 把 FileReader 创建对象移出作用域名,必须初始化为 null,否则 finally 处的 fr.close 会报编译错误
// 原因是,未初始化的对象不能调用 任何 方法
FileReader fr = null;
try {
// 但因 FileReader 本身也有编译异常,所以不能在 try 外面 new
fr = new FileReader("不存在的文件");
// 这里的代码不会被执行,所以 fr.close() 也不会被执行
fr.close();
}catch (IOException e){
e.printStackTrace();
}finally {
// 如果把 fr.close() 放在这里,则因为作用域的原因,会找不到 fr 对象
fr.close();
}
问题二:假如,try
中的 FileReader
创建依然报错,那么 fr 对象依然是null
,这时调用 finally
代码块中的 fr.close()
时,依然会报错
// 把 FileReader 创建对象移出作用域名
FileReader fr = null;
try {
// 但因 FileReader 本身也有编译异常,所以不能在 try 外面 new
fr = new FileReader("不存在的文件");
// 这里的代码不会被执行,所以 fr.close() 也不会被执行
fr.close();
}catch (IOException e){
e.printStackTrace();
}finally {
if (fr != null){
fr.close();
}
}
解决方法是:在 调用 close()
方法之前,判断 fr 是否为 null
异常中的处理(JDK7方式)
Java 也发现了普通方法中处理 close()
是比较麻烦的,所以在JDK 7 后,提供一个自动关闭文件接口 AutoCloseable
方法,FileReader
实现了 AutoCloseable
接口,我们不需要手动去调用 close()
方法
用法如下
try(定义字节流1; 定义字节流2) {
fr... 处理逻辑
fr2... 处理逻辑
}catch (IOException e){
}
在JDK7中,try
多出一个小括号,括号中编写定义字节流的方法,多个字节流定义,使用;
进行分割即可。
try(FileReader fr = new FileReader("不存在的路径");
FileReader fr2 = new FileReader("不存在的路径")) {
fis... 处理逻辑
fis2... 处理逻辑
}catch (IOException e){
}
不需要调用 close()
方法
异常中的处理(JDK9方式)
从JDK7的方案来看,确实比较好,但是在 try()
中传入创建方法,使得代码非常长,不利于阅读,在JDK9 中,Java 允许创建方法在try()
代码块外面定义,try()
中传个对象即可。
FileReader fr = new FileReader ("不存在的路径");
FileReader Stream fr2 = new FileReader ("不存在的路径");
try(fr; fr2) {
fr... 处理逻辑
fr2... 处理逻辑
}catch (IOException e){
}
多个对象,依然使用 ;
分割即可。
FileReader底层原理
1.当创建FileReader对象时,对象内部会生成一个 byte[8192] 的数组。
2.当调用第一次 read() 方法时,FileReader 会读取文件,并尽量填满数组中的 8192 个成员
3.当 read() 发现成员为 负数 时,说明读取到非英文字(或中文),read() 会根据字符集的规则,连续读取对应字节数(2~4字节,中文为3字节),并转为整型数字
4.在空参 read() 方法中,会直接把整型数字输出,如果是非空参 read(char[] buffc) 方法中,会存入用户事先提供的数组,并输出读取字节长度。
5.当 read() 方法已完成读取 byte[8192] 个成员后,再次读取文件中的数据,以次以尽可能的方式填满数组。
6.当第二次读取文件时,新的数据会覆盖数组成员,但如果第二次读取的数据没有填满 byte[8192] 个成员,则剩下的成员依然保留上一次读取的数据(即每一次读取文本不会清空数组)。
注意:FileInputStream 中的 read() 不具有 byte[] 数据缓冲机制。
示例:读取txt文件
FileReader fr = new FileReader("path");
int len;
// 定义字符数组,每次读取10个字出来
char[] c = new char[10];
while ((len = fr.read(c)) != -1) {
System.out.print(new String(c, 0, len));
}
共有 0 条评论