【文件IO】Java文件内容操作
-
-
- 一、文件流
-
- [1.1 InputStream](#1.1 InputStream)
- [1.2 OutputStream](#1.2 OutputStream)
- [1.3 Reader、Writer](#1.3 Reader、Writer)
- [1.4 用Scanner辅助](#1.4 用Scanner辅助)
-
一、文件流
主要分为字节流 (InputStream、OutputStream)和字符流 (Reader、Writer)。字节流每次读写的最小单位为1个字节,字符流每次读写的最小单位为1个字符(1个字符可能由多个字节构成)。
读写文件在各个编程语言中,都是固定套路。即打开文件、读写文件、关闭文件。关闭文件是十分重要的,在处理大量请求,大量的打开文件的时候,如果不及时关闭文件,可能会引起文件资源泄露:
文件资源泄露:
每个计算机上都维护有一张文件描述符表(顺序表/数组),记录当前打开了哪些文件。数组中的每个元素都是一个结构体,这个结构体描述了打开的文件在文件系统上的一些属性。每次打开一个文件都要在文件描述符表中占据一个位置,如果不及时关闭,就会导致文件描述符表被耗尽。如果耗尽,后续再打开文件就会打开失败。
此处的文件描述符表不能扩容:
因为对于操作系统内核来说,性能要求是相当高的,如果扩容(短时间内大量的搬运操作)引起卡顿、不稳定是得不偿失的。
1.1 InputStream
方法
修饰符及返回值类型 | ⽅法签名 | 说明 |
---|---|---|
int | read() | 读取⼀个字节的数据,返回 -1 代表已经完全读完了 |
int | read(byte[] b) | 最多读取 b.length 字节的数据到 b 中,返回实际读到的数量;-1 代表读完了 |
int | read(byte[] b, int off, int len) | 从off开始,读取len个字节的数据到b中,返回实际读到的数量;-1 代表读完了 |
void | close() | 关闭字节流 |
构造方法
InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输⼊设备都可以对应⼀个 InputStream 类,我们现在只关⼼从⽂件中读取,所以使⽤ FileInputStream。
签名 | 说明 |
---|---|
FileInputStream(File file) | 利⽤ File 构造⽂件输⼊流 |
FileInputStream(String name) | 利⽤⽂件路径构造⽂件输⼊流 |
- 打开关闭文件
java
//为了避免中间出现异常,close执行不到,将close写到finally中
InputStream inputStream=null;
try {
inputStream = new FileInputStream("./test.txt");
}finally {
inputStream.close();
}
//更方便的写法,try with resources
//实现了Closeable接口的类,才能放到try()里面
try(InputStream inputStream =new FileInputStream("./test.txt")){
//写中间逻辑
}
- 读写文件
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class IODemo3 {
public static void main(String[] args) throws IOException {
try(InputStream inputStream =new FileInputStream("./test.txt")){
while(true){
//每次读取一个字节
//此处返回的是读取的内容
// int b=inputStream.read();
// if(b==-1){
// //文件读取结束
// break;
// }
// System.out.printf("%d ",b);
//一次读多个字节,将内容读到buffer中
byte[] buffer=new byte[1024];
//此处返回的是读取的字节数
int n=inputStream.read(buffer);
if(n==-1){
//文件读取结束
break;
}
for(int i=0;i<n;i++){
System.out.printf("%d ",buffer[i]);
}
}
}
}
}
如果想要打印具体的内容而不是每个字节的十进制形式,可以构造字符串在打印。
java
String s=new String(buffer,0,n);
System.out.println(s);
1.2 OutputStream
方法
修饰符及返回值类型 | ⽅法签名 | 说明 |
---|---|---|
void | write(byte[] b) | 将 b 这个字符数组中的数据全部写⼊ os 中 |
int | write(byte[] b, int off, int len) | 将 b 这个字符数组中从 off 开始的数据写⼊ os 中,⼀共写 len 个 |
void | close() | 关闭字节流 |
void | flush() | 重要:我们知道 I/O 的速度是很慢的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写⼊内存的⼀个指定区域⾥,直到该区域满了或者其他指定条件时才真正将数据写⼊设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操作,将数据刷到设备中。 |
OutputStream 同样只是⼀个抽象类,要使⽤还需要具体的实现类。我们现在还是只关⼼写⼊⽂件中,所以使用FileOutputStream 。
java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class IODemo4 {
public static void main(String[] args) throws IOException {
//true表示追加,不加true表示覆盖
try(OutputStream outputStream=new FileOutputStream("./test.txt"),true){
byte[] buffer=new byte[]{97,98,99,100};
outputStream.write(buffer);
}
}
}
1.3 Reader、Writer
与上面的InputStream、OutputStream基本一致,但是操作单位为char(字符)。
我们来看几个例子:
java
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class IODemo5 {
public static void main(String[] args) throws IOException {
try(Reader reader=new FileReader("./test.txt")){
while(true){
char[] buffer=new char[1024];
int n=reader.read(buffer);
if(n==-1){
break;
}
// String s=new String(buffer,0,n);
// System.out.println(s);
for(int i=0;i<n;i++){
System.out.print(buffer[i]+" ");
}
}
}
}
}
运行结果:
我们知道,一个汉字占3个字节,而一个char只占两个字节,为什么这里没出问题呢?
Java中将utf8先转化为unicode(2字节),再转化为utf8.
java
try(Writer writer=new FileWriter("./test.txt",true)){
String s="坤坤";
writer.write(s);
}
1.4 用Scanner辅助
使用Scanner,我们就不需要构造空数组来接收输入,而是可以用Scanner.next()来完成这一过程。
java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class IODemo6 {
public static void main(String[] args) throws IOException {
try(InputStream inputStream=new FileInputStream("./test.txt")){
Scanner scanner=new Scanner(inputStream);
while(scanner.hasNext()){
String s=scanner.next();
System.out.println(s);
}
}
}
}
我们之前常写的System.in
、System.out
其实就是输入输出流。