文件操作 和 IO

news2025/3/16 7:22:50

目录

​编辑一、认识文件

1、文件路径

2、其它知识

二、Java 中操作文件

三、文件内容的读写

1、Reader

2、InputStream

3、输出


一、认识文件

 文件是在硬盘上存储数据的一种方式,操作系统帮我们把硬盘的一些细节都封装起来了

我们只需要了解文件相关的一些接口即可

硬盘是用来存储数据,和内存相比,硬盘的存储空间更大,访问速度更慢,成本更低,可以持久化存储

操作系统通过 “文件系统” 这样的模块来管理硬盘

实际上我的电脑只有一个硬盘,操作系统可以通过文件系统把这个硬盘抽象成多个硬盘一样 NTFS 是windows 上的文件系统,背后有一定的格式来组织硬盘数据

 EXT4 是 Linux 上常见的文件系统


1、文件路径

不同的文件系统,管理文件的方式都是类似的

通过 目录 - 文件 构成了 “ N 叉树” 树形结构

通过 D盘 - tmp - cat.jpg 这个路线,就能 找到 / 确定 电脑上的唯一一个文件,这个东西就称为 “路径”

在 windows 上,使用 / 或者 \ 来分割不同的目录

以盘符开头的路径,也叫做 “绝对路径”

绝对路径相当于与是从 “此电脑” 这里出发,找文件的过程

以 . 或者 ... 开头的目镜,叫做 “绝对路径”

相对路径,需要有一个 “基准目录” / “工作目录”,表示从这个基准出发,怎么走能找到这个文件夹

如果以 D:为基准目录 ./tmp/cat.jpg  如果以 D:tmp 为基准 ./cat.jpg (. 表示当前所在目录)

如果以 D:/tmp/111 为基准, ..cat.jpg(..表示当前目录的上一级目录)

同样是一个 cat.jpg 文件,站在不同的基准目录上找到的路径是不一样的


2、其它知识

文件系统上存储的文件,又可以分成两个大类

1、文本文件

存储的是字符

例如:utf-8 就是一个大表,这个表上的数据的组合,就可以称为字符

2、二进制文件

存储的是二进制的数据

如何判断文件是文本文件还是二进制文件?

一个最简单的方式:直接使用记事本打开

记事本打开文件,就是尝试把当前的数据,在码表中查询

如果打开之后能看懂,就是文本文件,如果打开之后看不懂,就是二进制文件


二、Java 中操作文件

后续针对文件的操作,文本和二进制的操作方式是不同的

文件系统操作:创建文件,删除文件,创建目录....

java 中,不要通过 java.io.file 类来对一个文件(包括目录)进行具体的描述

IO : input 和 output,我们是站在 cpu 的视角来看待输入输出的

通过 File 对象来描述到一个具体的文件

File 对象可以对应到一个真实存在的文件,也可以对应到一个不存在的文件

构造方法:

第二个构造方法此处的字符串,就表示一个路径,可以是绝对路径,也可以是相对路径

方法: 

站在操作系统的角度来看待:目录也是文件

操作系统的文件是一个更广义的概念,具体来说有很多不同的类型

1、普通文件(通常见到的文件)

2、目录文件(通常见到的文件夹)

windows 上,目录之间的分隔符,可以使用 / 也可以使用 \ 

Linux 和 mac 上面,就只支持 /

所以即使在 windows 上,也尽量使用 / ,使用 、 在代码中需要搭配转义字符

可以通过代码,来感受一下对文件的操作: 当我们把绝对路径改成相对路径,代码运行结果又会有所不同:

 getAbsolutePath 会将工作目录拼接上当前目录,就是运行的结果

在 idea 中运行一个程序,工作目录就是项目所在的目录,在命令行中 运行一个程序,工作目录就是命令行当前所在的目录,如果程序是运行在 tomacat 中,工作目录就是 tomcat 下的 bin 目录

这个操作是可能会抛出异常的

比如,当前写入的路径是一个非法的路径

比如,当前创建的这个文件,对于所在的目录没有权限操作

有的时候,可能会用到这样的一个功能:临时文件

程序运行的时候,搞一个临时文件,程序结束了,临时文件中自动删掉

像 office 等很多这样的生产力软件 都有产生临时文件功能,这个临时文件就自动保存了你当前的编辑的中间状态

有的人使用 word 不喜欢保存,用了一段时间之后,电脑突然断电关机了,没保存的数据难道就没了吗?

重启电脑,由于刚才是非正常关闭,临时文件是来不及删除,仍然存在的moffice 启动就能知道上次是异常关闭了,就会提示你是否要从之前的临时文件恢复未保存的数据 

 创建目录代码:

import java.io.File;
import java.io.FileReader;

public class Demo4 {
    public static void main(String[] args) {
        File file = new File("./test-dir");
        //mk -> make dir->directory
        //mkdir 一次只能创建一层目录,mkdirs 可以一次创建多层目录
        file.mkdir();
        //file.mkdirs();
    }
}

 文件重命名也可以起到文件移动的效果

import java.io.File;

//文件重命名
public class Demo5 {
    public static void main(String[] args) {
        File file = new File("./test.txt");
        File file2 = new File("./src/test2.txt");
        file.renameTo(file2);
    }
}

以上文件系统的操作,都是基于 File 类来完成的

另外还需要文件内容的操作


三、文件内容的读写

1、Reader

文件这里的内容本质是来自于硬盘,硬盘又是操作系统管理的,使用某个编程语言操作文件,本质上都是需要调用系统的 api 

虽然不同的编程语言操作文件的 api 有所差别,但是基本步骤都是一样的

文件内容操作的核心步骤有四个:

1、打开文件

2、关闭文件

3、读文件

4、写文件

Java IO流 是一个比较庞大的体系,涉及到非常多的类,这些不同的类,都有各自不同的特性,但是总的来说,使用的方法都是类似的

(1)构造方法,打开文件

(2)close 方法,关闭文件

(3)如果衍生自 InputStream 或者 Read ,就可以使用 read 方法来读数据

(4)如果衍生自 OutputStream 或者 Writer ,就可以使用 write 方法来写数据

 读文件操作:

 这个操作非常重要,释放必要的资源

让一个进程打开一个文件,是要从系统这里申请一定的资源的(占用进程的 pcb 的文件描述符表中的一个表项)

如果不释放,就会出现 “文件资源泄露” ,是一个很严重的问题

文件描述符表 可以理解成一个顺序表,长度有限,不会自动扩容,一旦一直打开文件,而不去关闭不用的文件,文件描述符表就会被占满,后续就没法打开新的文件了

我们可以使用 try with rewsourses 来避免出现上述问题

此时只要 try 代码执行完毕了,就会自动调用到 close 方法

文件流中的任意对象,都可以按照上述讨论来进行 close

一次读一个 char 类型的数组

 会把读到的内容,填充到参数的这个 cbuf 数组中,此处的参数,相当于是一个输出形参数

通过 read ,就会把本来是空的一个数组,填充上内容

n 表示实际读到的字符的个数

相当于给了 1024 这么大空间,如果文件足够大,超过 1024,就能填满这个空间

如果文件比较小,不足1024,就会把文件所有内容都填到数组中(剩下会空余)

返回值 n 就表示实际读到的字符的个数

有时候,可能会涉及到有多个小文件,都需要读取并且拼接到一起,就可以使用这个方法

假设现在有三个文件,每个文件的大小是 100 字节

 最终代码如下:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

//Reader 的使用
public class Demo6 {
    public static void main(String[] args) throws IOException {
/*        //FileReder 的构造方法,可以填写一个路径(绝对路径和相对路径都可以),也可以写一个构造好了的 file 对象
        Reader reader = new FileReader("d:/test.txt");
        try {
            //中间的代码无论出现什么情况,close 都可以执行到
        }finally {
            //如果抛出异常或者 return ,close就都执行不到了
            reader.close();
        }*/

        //上述使用 finally 的方式能解决问题,但是不优雅
        //使用 try with resourses 是更好的解决方法
        try(Reader reader = new FileReader("d:/test.txt")){
            while(true){
                char buf[] = new char[1024];
                int n = reader.read(buf);
                if(n == -1){
                    //读到文件末尾了
                    break;
                }
                for(int i = 0;i < n;i++){
                    System.out.print(buf[i] + " ");
                }
            }
        }
    }
}

2、InputStream

InputStream 是字节流,用法和 Reader 相似

文本文件,也可以使用字节流打开,只不过此时你读到的每个字节,就不是完整的字符了

 一次读一个字节

 一次读若干个字节,尝试填满这个 byte[]

一次读若干个字节,填满数组的一部分

Java 虽然有 char ,但是很少会用,更多的是使用 String 

此处,我们可以借助一些额外的工具类,就可以完成 字节 / 字符 --> 字符串 的转化

虽然也可以直接使用 String 的构造方法完成 char[] 或者 byte[] 到字符串的转化,但是比较麻烦

这个工具类就是 Scanner !!!

操作系统中,所谓的文件,是一个广义的概念,System.in 是一个特殊的文件,对应到 “标准输入”,普通的硬盘上的文件,也是文件

后来网络编程中的网卡(socket),也是文件

Scanner 都是一视同仁的,只是把当前读到的字节数据进行转换,但并不关心这个数据是来自于哪里

但是,要注意,Scanner 只是用来读取文本文件的,不是适合读取二进制文件

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Demo8 {
    public static void main(String[] args) throws IOException {
        try(InputStream inputStream = new FileInputStream("d:/test.txt")){
            Scanner scanner = new Scanner(inputStream);
            //此时就是从 test.txt 这个文件中读取数据了
            String s = scanner.next();
            System.out.println(s);
        }

    }
}

3、输出

输出的使用方法和输入非常相似,关键操作是 write 

write 之前要打开文件,write 之后也需要关闭文件

输出流对象(无论是字节流还是字符流),会在打开文件之后,清空文件内容!!!

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Demo8 {
    public static void main(String[] args) throws IOException {
        try(InputStream inputStream = new FileInputStream("d:/test.txt")){
            Scanner scanner = new Scanner(inputStream);
            //此时就是从 test.txt 这个文件中读取数据了
            String s = scanner.next();
            System.out.println(s);
        }

    }
}

还可以按照追加写的方式打开,此时就不会清空内容了 

读操作和写操作,也都能支持随机访问,可以移动光标到指定位置进行读写,此处就不进行介绍了

OutputStream 方式使用方法完全一样

只不过 write 方法,不能支持 字符串 参数,只能按照 字节 或者 字节数组来写入


实例:扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除此文件

import java.io.File;
import java.util.Scanner;

public class Demo10 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //1、用户输入一个目录,后续的查找都是针对这个目录进行的
        System.out.println("请输入要搜索的目录");
        File rootPath = new File(scanner.next());

        //2、再让用户输入要搜索和删除的关键粗
        System.out.println("请输入要删除的关键字");
        String word = scanner.next();

        //3、判断当前目录是否有效
        if (!rootPath.isDirectory()){
            System.out.println("此时输入的路径不是合法目录");
            return;
        }

        //4、遍历目录,从根目录出发,按照深度优先(递归) 的方式进行
        scanDir(rootPath,word);
    }
    public static void scanDir(File currentDir,String word){
        //1、先列出当前目录中包含哪些内容
        File[] files = currentDir.listFiles();
        if (files == null || files.length == 0){
            //空的目录或者非法的目录
            return;
        }
        //2、遍历列出的文件,分两个情况进行讨论
        for(File f : files){
            if (f.isFile()){
                //如果当前文件是普通文件,看看文件名是否包含了 word ,来决定是否要删除
                dealFile(f,word);
            }else {
                //如果当前文件是目录文件,就递归执行 scanDir
                scanDir(f,word);
            }
        }
    }
    public static void dealFile(File f,String word){
        Scanner scanner = new Scanner(System.in);
        //1、先判断当前文件是否包含 word
        if (!f.getName().contains(word)){
            //此时文件不包含 word,
            return;
        }
        //2、包含 word,询问用户是否删除该文件
        System.out.println("该文件是: " + f.getAbsolutePath() + ",是否确认删除(Y / N )");
        String choice = scanner.next();
        if (choice.equals("Y") || choice.equals("y")){
            f.delete();
        }
    }
}

示例:进行普通的文件复制

import java.io.*;
import java.util.Scanner;

public class Demo11 {
    public static void main(String[] args) throws IOException {
        System.out.println("请输入要复制的文件路径");
        Scanner scanner = new Scanner(System.in);
        String src = scanner.next();
        File srcfile= new File(src);
        if (!srcfile.isFile()){
            System.out.println("您输入的源文件路径是非法的");
            return;
        }
        System.out.println("请输入要复制到的目标路径");
        String dest = scanner.next();
        File destfile= new File(dest);
        //不要求目标文件存在,但是得保证目标文件所在的目录是存在的
        if (!destfile.getParentFile().isDirectory()){
            System.out.println("您输入的目标文件路径是非法的");
            return;
        }

        //2、进行复制操作的过程,按照字节流打开
        try(InputStream inputStream = new FileInputStream(srcfile);
            OutputStream outputStream = new FileOutputStream(destfile)){
                while(true){
                    byte[] buffer = new byte[1024];
                    int n = inputStream.read(buffer);
                    if (n == -1){
                        System.out.println("读取到 eof,循环结束");
                        break;
                    }
                    outputStream.write(buffer,0,n);
                }
            }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/883422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

stack+queue

适配器 介绍 在C的标准模板库&#xff08;STL&#xff09;中&#xff0c;有几种适配器&#xff0c;它们是一些容器或函数对象的包装&#xff0c;提供了不同的接口和功能&#xff0c;用于适应特定的需求 分类 STL中的适配器可以分为两类&#xff1a;容器适配器和迭代器适配器 容…

PHP自己的框架实现function引入和dump函数(完善篇一)

1、实现效果 2、创建三个function.php 3、文件加载&#xff08;KJ.php&#xff09; 定义目录 define("FILE_PATH",KJ_CORE./file); //定义框架文件路径define("COMMON_PATH",ROOT_PATH./common); //定义公共目录 加载文件 public static function run(){…

一种多策略下RabbitMQ的延时队列实现

1.为什么会用到延时队列? 场景: 最近在开发一款系统中遇到这样一个场景,A系统开通套餐需要把套餐信息以邮件的形式发送给相关工作人员,经过人工审核通过后,在B系统里面开通,A系统会调B系统套餐列表接口查询套餐是否开通成功,开通成功则从A系统去完成订单,假如超过设定时间未开…

vue 发现页面找不到3秒后跳转到本页面

这个路由跳转用到的是编程式跳转this.$router.push 两种写法&#xff1a; 第一种可以通过path来跳转 goto(/find) find是路由里边的路径 <span click"goto(/find)">发现音乐</span> <span click"goto(/my)">我的音乐</span> <…

Virtualbox设置访问外网以及主机和虚拟机互通

参考链接 1、设置使虚拟机访问外网。选中虚拟机&#xff0c;右击选择“设置”。 2、在设置中选择“网络”&#xff0c;然后点击“网卡1”&#xff0c;选择“网络地址转换&#xff08;NAT&#xff09;”模式&#xff0c;点击“确定”。 4.此时你的虚拟机就可以访问外网了 5…

NRF24L01+数据手册_关于几种工作模式

使用的是官方数据手册的章节编号&#xff0c;原文截图方便对照&#xff0c;部分翻译&#xff08;标蓝&#xff09;、个人理解&#xff08;标紫&#xff09;&#xff0c;关键信息&#xff08;标红&#xff09;。 6.1 Operational Modes操作模式 6.1.1 State diagram状态机图 6…

sqlloader学习笔记

INFILE的用法 1&#xff09;模糊导入多个数据的文件。 可以在文件名中使用通配符。 星号 &#xff08;*&#xff09; 表示复数字符&#xff0c;问号 &#xff08;&#xff1f;&#xff09; 表示单个字符。 INFILE emp*.dat INFILE m?emp.dat 2&#xff09;如果不需要导入数据…

麒麟arm架构 编译安装qt5.14.2

1、先在官网下载qt源码&#xff1a; https://download.qt.io/archive/qt/5.14/5.14.2/single/[qt源码下载地址] 2、解压编译 使用tar -xvf qt-everywhere-src-5.14.2.tar.xz 解压压缩包 cd qt-everywhere-src-5.14.2 执行 ./configure --prefix/usr/local/qt.5.14.2 make -…

【Nginx17】Nginx学习:目录索引、字符集与浏览器判断模块

Nginx学习&#xff1a;目录索引、字符集与浏览器判断模块 今天要学习的内容有几个还是大家比较常见的&#xff0c;所以学习起来也不会特别费劲。对于目录的默认页设置大家都不会陌生&#xff0c;字符集的设置也比较常见&#xff0c;而浏览器的判断这一块&#xff0c;可能有同学…

大模型基础:GPT家族与提示学习

大模型基础:GPT 家族与提示学习 从 GPT-1 到 GPT-3.5 GPT(Generative Pre-trained Transformer)是 Google 于2018年提出的一种基于 Transformer 的预训练语言模型。它标志着自然语言处理领域从 RNN 时代进入 Transformer 时代。GPT 的发展历史和技术特点如下: GPT-12018年6月…

OpenSSH 远程升级到 9.4p1

OpenSSH 远程升级到 9.4p1 文章目录 OpenSSH 远程升级到 9.4p1背景升级前提1. 升级 OpenSSL2. 安装并启用Telnet 升级OpenSSH 背景 最近的护网行动&#xff0c;被查出来了好几个关于OpenSSH 的漏洞。这是因为服务器系统安装后&#xff0c;直接使用了系统自带版本的OpenSSH &am…

lib调试报LNK2038 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“0”不匹配值“2”

最近用cef&#xff0c;要debug调试&#xff0c;引用库时&#xff0c;提示&#xff1a; LNK2038 检测到“_ITERATOR_DEBUG_LEVEL”的不匹配项: 值“0”不匹配值“2” 研究后的结论&#xff1a;这是因为&#xff0c;这个库的实现方式太老&#xff1a; #if _HAS_ITERATOR_DEBUG…

【C++笔记】C++之类与对象(上)

【C笔记】C之类与对象&#xff08;上&#xff09; 1、类是结构体的升级2、类中可以定义的东西3、类访问限定符4、类的声明5、类的实例化(定义)6、类的大小的计算7、this指针 1、类是结构体的升级 C的一个显著特征就是兼容C语言&#xff0c;所以C把结构体“升级”成了“类”&am…

高等数学 | 微分方程解决单中值问题、高阶导数的莱布尼兹公式

单中值问题都可以用通过求解微分方程的特解构造辅助函数&#xff0c;再用罗尔定理即可。 高阶导数的莱布尼兹公式推导以及应用&#xff0c;先求导至能够发现某次求导开始为0的时候&#xff0c;对其使用莱布尼兹公式。

Photoshop制作漂亮光泽感3D按钮

原文链接(https://img-blog.csdnimg.cn/45472c07f29944458570b59fe1f9a0e0.png)

简单记录牛客top101算法题(初级题C语言实现)BM24 二叉树的中序遍历 BM28 二叉树的最大深度 BM29 二叉树中和为某一值的路径

1. BM24 二叉树的中序/后续遍历 要求&#xff1a;给定一个二叉树的根节点root&#xff0c;返回它的中序遍历结果。                          输入&#xff1a;{1,2,#,#,3} 返回值&#xff1a;[2,3,1]1.1 自己的整体思路&#xff08;与二叉树的前序遍…

HCIP——BGP协议

目录 一.什么是BGP&#xff1f; 二.IGP和EGP 三.BGP的特点 四.BGP的数据包 五.BGP的工作过程 BGP的六种状态机 六.BGP的路由黑洞问题 七.BGP的防环机制——水平分割 八.BGP的宣告问题 九.BGP的自动汇总 十.BGP的安全特性 十一.BGP的选路规则 一.什么是BGP&#xff1f; 1.…

ATF(TF-A)安全通告 TFV-8 (CVE-2018-19440)

安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-8 (CVE-2018-19440) 二、CVE-2018-19440 一、ATF(TF-A)安全通告 TFV-8 (CVE-2018-19440) Title 不保存x0~x3寄存器可能会将信息从一个非安全世界的SMC client泄漏到另一个 CVE ID …

学习ts(二)数据类型(接口和对象类型、数组类型)

interface 重名会重合到一起 如果两个interface名称相同&#xff0c;会把两个合到一起 重复定义同一个需要类型相同 不能多或者减少属性 设置任意key 当定义接口返回数据时&#xff0c;我们不确定接口会返回多少&#xff0c;知道所需要的固定属性&#xff0c;其余属性可以…

第九章 动态规划part08(代码随想录)

139.单词拆分 1. 确定dp[i][j] dp数组以及下标的含义一维dp数组的递推公式 dp[i] : 字符串长度为i的话&#xff0c;dp[i]为true&#xff0c;表示可以单词能被在字典中出现的单词组成。 dp[s.size()] true; 说明可以利用字典中出现的单词拼接出 s 。 2. 一维dp数组的递推公式…