【文件IO】Java文件内容操作

【文件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.inSystem.out其实就是输入输出流。

相关推荐
Chen-Edward10 分钟前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
陈小桔1 小时前
idea中重新加载所有maven项目失败,但maven compile成功
java·maven
小学鸡!1 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
xiaogg36781 小时前
阿里云k8s1.33部署yaml和dockerfile配置文件
java·linux·kubernetes
逆光的July2 小时前
Hikari连接池
java
微风粼粼2 小时前
eclipse 导入javaweb项目,以及配置教程(傻瓜式教学)
java·ide·eclipse
番茄Salad2 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
天若有情6732 小时前
Spring MVC文件上传与下载全面详解:从原理到实战
java·spring·mvc·springmvc·javaee·multipart
祈祷苍天赐我java之术2 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
Olrookie3 小时前
若依前后端分离版学习笔记(二十)——实现滑块验证码(vue3)
java·前端·笔记·后端·学习·vue·ruoyi