Java中的文件IO

文件,我们之前在C语言中接触过,是在硬盘上存储数据的方式,操作系统帮我们把硬盘的一些细节都封装起来了,因此在这里我们只需要了解文件的相关接口即可.

首先硬盘是用来存储数据的,和内存相比,硬盘的存储空间更大,访问速度更慢,成本更低,可以实现持久化存储,而操作系统通过"文件系统"这样的模块来管理硬盘.

实际上在我们的电脑中只有一个硬盘(我这里加了一块,所以我有两块)操作系统可以通过文件系统吧这个硬盘抽象成多个硬盘一样(其实CDE盘就是操作系统通过文件系统锁划分出来的) 

NTFS是Windows上的文件系统,背后有一定的格式来组织硬盘的数据 

EXT4是Linux上常见的文件系统,虽然说有不同的文件系统,但是其管理文件的方式都是类似的,那么文件在系统中是一个什么样的结构呢?

答:通过目录(也就是我们口头说的文件夹)-文件构成了"N叉树"的树形结构.

我们可以看到,同样是一个cat.jpg的文件,站在不同的基准目录上,查找的路径是不相同的

文件系统上存储的文件,具体来说又分成两个大类.

1.[文本文件]存储的是字符,字符通常采用utf-8来编码,utf8就是一个大表,这个表上的数据组合就可以称为是字符

2.[二进制文件]存储的是二进制的数据

那我们如何判断一个文件是文本文件还是二进制文件呢?其实也很简单,用记事本打开,你能看懂的就是文本文件,若是乱码,则就是二进制文件

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

文件系统操作

首先要声明一个视角问题

IO:其实是input和output,你是站在cpu的角度来看待输入输出的

在Java中,我们可以通过java.io.file,通过file对象来描述一个具体的文件.file对象可以对应到一个真实存在的文件,也可以对应到一个不存在的文件

站在操作系统的角度看待,目录也是文件,操作系统中的文件是一个更广义的概念,具体来说里面有很多种不同的类型

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

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

windows上目录之间的分隔符,可以使用/也可以使用\,Windows诞生之前,表示目录的分隔符都是用 / Windows前身是dos.Linux和Mac就只是支持 / ,所以及时是在Windows,也尽量使用/,使用\在代码中需要搭配转义字符

运行结果如下

上述创建文件我们是使用了一个绝对路径,差别不是那么特别大,接下来我们使用相对路径来创建一个文件

这里是把工作目录拼接到当前目录,在IDEA中运行一个程序,工作目录就是项目所在的目录,那么加入我们在命令行中运行一个程序,工作目录就是命令行所在目录,在上述两个例子中我们可以看到,getCanonicalFile()本着上也是一个绝对目录,只不过getCanonicalFile将geyAbsoluterFile()得到的绝对目录整理了一下,使得其更加简洁

注意,我们并不是说将要创建的文件的文件路径写到构造方法中这个文件就创建好了,想要真正去创建一个文件还需要如下操作

创建文件操作

 此时才是真正创建好了一个文件,当然了createNewFile是可能会抛出一个异常的,比如你当前写入的路径是一个非法路径,比如,创建的这个文件,对于所在的目录没有权限操作

文件创建好之后左边会有显示的 

删除文件

左边的显示没有了

还有一种删除方式就是这个样子,在程序中检测到有这个文件,但是将程序结束之后会将这个文件删除,所以就造成了上述这种现象.那么这个东西有什么用呢?我们有的时候需要创建一个临时文件,就会用到这个功能

创建目录

创建一层目录

创建多层目录

重命名

现在test是与src同级的

代码运行之后,我们给这个文件重命名了,当然我们在这里看到的是我们把文件移动了位置,这是它的另外一种功能

以上文件系统的操作,都是基于File类来完成的,我们还需要针对文件内容的操作

文件流

什么是流?(stream)

文件这里的内容本质是来自于硬盘,硬盘又是操作系统管理的.使用某个编程语言操作文件,本质上都是需要调用系统API.虽然不同的编程语言操作文件的API有所差别,但基本步骤都是一样的,文件内容的核心操作步骤有四个

1.打开文件

2.关闭文件

3.读文件

4.写文件

在Java中操作文件内容也是有一系列的类的,但主要分为两个大类

字节流

inputStream 和 OutputStream 后续的一些操作字节的类都是衍生自这两个类 是以操作字节为单位的.(主要针对二进制文件)

字符流

Reader 和 Writer 后续操作字符的类,衍生自这两个类 是以操作字符为单位的.(主要针对文本文件)

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

1.构造方法,打开文件

2.close方法,关闭文件

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

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

我们先尝试一下创建一个文本文件使用一下,这个操作会抛出FileNotFoundException这样的一个异常

我们若是要把这个文件关闭,此时要抛出IOException

其实我们不难想到,FileNotFoundException是IOException的一个子类

注意close这个操作是非常重要的,这个操作是用来释放必要的资源的

让一个进程打开一个文件,是要从系统这里申请一定的资源的.(占用进程的pcb里的文件描述符表(本质上是一个顺序表,而且长度有限,不会自动扩容)中的一个表项)

如果不释放,就会出现"文件资源泄露"这样的很严重的问题.

一旦一直打开文件,而不去关闭不用的文件,文件描述符表就会终有一天被占满,后续就无法打开新的文件了

使用try{}finally{}来避免这样的情况

但是这样的操作不够优雅我们可以这样写,这样写的话只要代码执行完了,就会自动调用到close方法,这里的设定类似于synchronized,当然了try();()里面可以写多个打开文件的操作,而且文件流中的任意对象,都可以按照上述的方法来进行close.

读文件

reader

reader的返回值是int,假如你现在的文件中的内容不足1024那么就会把文件所有的内容都填到数组中(剩下的会有空余),此时返回值就会表示实际读到的字符的个数

InputStream

InputStream是字节流,用法和Reader非常相似,注意文本文件也是可以使用字节流打开,只不过此时你读到的每个字节,就不是完整的字符了

我们可以看到,数据是正确的

但是Java虽然有char,但是我们很少会用,更多的是使用String,此处,借助一些额外的工具类就可以完成字节/字符=>字符串的转换,虽然我们也可以直接使用String的构造方法完成char[]或者byte[]=>字符串的转换,比较的麻烦也不优雅,String的构造方法进行转换的时候,实际上构造方法内部是对字节数组进行了编码转换,当然了我们也可以使用工具类来进行操作

Scanner

所以我们就可以这样来写

以前学过的Scanner的操作,在这里完完全适用,但是需要注意Scanner只是用来读文本文件的,不适合读取二进制文件.

写文件

输出,使用的方法和输入非常相似,关键操作是write,write之前要打开文件,用完了也要关闭文件

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

我们还可以按照追加写方式打开,此时就不会清空文件内容

此外读操作和写操作也是支持"随机访问"的,也就是说可以移动光标到指定位置,进行读写(此处就不加介绍了)

OutputStream使用方式完全一样,只不过write方法,不能支持字符串参数,只能按照字节或者字节数组来写入

练习

1.扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件(经典面试题)

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

/**
 * Created with IntelliJ IDEA
 * Description:
 * User: lenovo
 * Date: 2024 -01 -13
 * Time: 13:42
 */
public class Test {
    public static Scanner scanner=new Scanner(System.in);
    public static void main(String[] args) {
        System.out.println("请输入您要查找的目录:");
        File rootPath=new File(scanner.next());
        if (!rootPath.isDirectory()){
            System.out.println("该目录无效");
            return;
        }
        System.out.println("请输入你要查找的关键字:");
        String word =scanner.next();
        //创建一个遍历目录的方法
        scanDir(rootPath,word);
    }
//遍历目录
    public static void scanDir(File rootPath,String word) {
        //将rootPath目录下所有文件列出来
        File[] cur=rootPath.listFiles();
        if(cur.length==0||cur==null){
            System.out.println("该目录为空");
            return;
        }
        //深度优先遍历(一条路走到头后再转过来走另一条路)(也就是递归)
        for (File f: cur) {
            System.out.println(f.getAbsolutePath());
            if(f.isFile()){
                //题录中要求必须是文件,不可以是目录,所以这里要判断是文件还是目录
                delDir(f,word);
            }else{
                scanDir(f,word);
            }
        }
    }

    private static void delDir(File f,String word) {
        //来判断文件名中是否包含所要查找的关键字
        if(f.getName().contains(word)){
            System.out.println("是否要删除"+f.getAbsolutePath()+"?(Y/N)");
            String choice=scanner.next();
            if(choice=="Y"||choice=="y"){
                f.delete();
                System.out.println("删除成功");
            }else{
                System.out.println("已取消删除");
            }
        }
    }
}

2.进行普通文件的复制

把一个文件,复制成另一个文件,也就是打开文件A,一次读出每一个字节,再写入到文件B就可以了

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

/**
 * Created with IntelliJ IDEA
 * Description:
 * User: lenovo
 * Date: 2024 -01 -13
 * Time: 15:28
 */
public class Test {
    public static void main(String[] args) throws IOException {
        System.out.println("请输入你的源路径:");
        Scanner scanner=new Scanner(System.in);
        File src=new File(scanner.next());
        if(!src.isFile()){
            System.out.println("当前路径非法!");
            return;
        }
        System.out.println("请输入你的目标路径:");
        File dest=new File(scanner.next());
        try(InputStream inputStream=new FileInputStream(src);OutputStream outputStream=new FileOutputStream(dest)){
           byte[] bytes=new byte[2048];
           while(true){
               int n=inputStream.read(bytes);
               System.out.println("n="+n);
                if(n==-1){
                    System.out.println("读取到eof,读取结束");
                    return;
                }
                outputStream.write(bytes,0,n);
           }
        }
    }
}

注意,每次进行read都是在访问硬盘,访问硬盘所造成的开销是比较大的,此时我们把bytes变大,就能降低访问的次数,从而提高效率,但是bytes大的前提是内存空间足够

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Polkadot 安全机制揭秘:保障多链生态的互操作性与安全性

作者:Filippo Franchini,Web3 Foundation 原文:https://x.com/filippoweb3/status/1806318265536242146 编译:OneBlock Polkadot 是一个创新的多链区块链平台,旨在实现不同区块链之间的互操作性和共享安全性。本文将详…

c++习题02-浮点数求余

目录 一,问题 二,思路 三,代码 一,问题 二,思路 虽然在浮点类型中没有取余的运算(无法直接使用%符号取余),但是我们都知道在数学中,除法是减法的连续运算&#xff…

软件测试最全面试题及答案整理(2024最新版)

1、你的测试职业发展是什么? 测试经验越多,测试能力越高。所以我的职业发展是需要时间积累的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年积累测试经验,按如何做好测试工程师的要点去要求自己,不断…

师从IEEE fellow|博士后加拿大阿尔伯塔大学成行

V老师指定申请加拿大,优先对方出资的博士后,如果外方无资助,也可以自筹经费,但要求必须是博士后头衔。最终我们为其落实了加拿大阿尔伯塔大学的postdoctoral fellow(博士后研究员),尽管是无薪职…

经典链表算法题:找到环的入口。清晰图示推导出来

Leetcode题目链接 原理 重画链表如下所示,线上有若干个节点。记蓝色慢指针为 slow,红色快指针为 fast。初始时 slow 和 fast 均在头节点处。 使 slow 和 fast 同时前进,fast 的速度是 slow 的两倍。当 slow 抵达环的入口处时,如…

前端播放RTSP视频流,使用FLV请求RTSP视频流播放(Vue项目,在Vue中使用插件flv.js请求RTSP视频流播放)

简述:在浏览器中请求 RTSP 视频流并进行播放时,直接使用原生的浏览器 API 是行不通的,因为它们不支持 RTSP 协议。为了解决这个问题,开发者通常会选择使用像 flv.js 这样的库,它专为在浏览器中播放 FLV 和其他流媒体格…

4款引以为豪的办公软件,使用起来,舒适度满满

Everything 是Windows神级搜索软件,能做到秒级响应。 Everything 之前小编在文章里提过好几次,但还有很多小伙伴不知道,那就再给大家种草一下哈。 只需要打开一次,Everything就会自动为你的文件建立索引,之后&#…

Spring MVC 中使用 RESTFul 编程风格

1. Spring MVC 中使用 RESTFul 编程风格 文章目录 1. Spring MVC 中使用 RESTFul 编程风格2. RESTFul 编程风格2.1 RESTFul 是什么2.2 RESTFul风格与传统方式对比 3. Spring MVC 中使用 RESTFul 编程风格(增删改查)的使用3.1 准备工作3.2 RESTFul 风格的 “查询” 所有&#xf…

概率论与数理统计_下_科学出版社

contents 前言第5章 大数定律与中心极限定理独立同分布中心极限定理 第6章 数理统计的基本概念6.1 总体与样本6.2 经验分布与频率直方图6.3 统计量6.4 正态总体抽样分布定理6.4.1 卡方分布、t 分布、F 分布6.4.2 正态总体抽样分布基本定理 第7章 参数估计7.1 点估计7.1.1 矩估计…

视频网关的作用

在数字化时代,视频通信已经成为了人们日常生活和工作中的重要部分。为了满足不同设备和平台之间的视频通信需求,各种视频协议应运而生。然而,这些协议之间的差异使得相互通信变得复杂。因此,视频网关作为一种重要的网络设备&#…

使用TensorRT进行加速推理(示例+代码)

目录 前言 一、TensorRT简介 1.1TensorRT 的主要特点 1.2TensorRT 的工作流程 二、具体示例 2.1代码 2.2代码结构 2.3打印结果 前言 TensorRT 是 NVIDIA 开发的一款高性能深度学习推理引擎,旨在优化神经网络模型并加速其在 NVIDIA GPU 上的推理性能。它支持…

告别写作难题,这些AI写作工具让你文思泉涌

在现实生活中,除了专业的文字工作者,各行各业都避免不了需要写一些东西,比如策划案、论文、公文、讲话稿、总结计划……等等。而随着科技的进步,数字化时代的深入发展,AI已经成为日常工作中必不可少的工具了&#xff0…

Django创建项目(1)

运行 注意 在本次创建Django项目时,出现了一点小问题,由于我之前pip换源过,换源用的是http,结果在创建时,pip只支持https,所以如果出现创建项目失败的问题,那么有可能是因为换源的问题&#xf…

electron-vue自定义标题

1.在主进程background.js或者main.js中主窗口配置frame: false async function createWindow() {Menu.setApplicationMenu(null);// Create the browser window.const win new BrowserWindow({width: 1000,height: 600,resizable: false,frame: false,webPreferences: {nodeI…

【CSS in Depth 2 精译】2.3 告别像素思维

当前内容所在位置 第一章 层叠、优先级与继承第二章 相对单位 2.1 相对单位的威力 2.1.1 响应式设计的兴起 2.2 em 与 rem 2.2.1 使用 em 定义字号2.2.2 使用 rem 设置字号 2.3 告别像素思维 ✔️2.4 视口的相对单位2.5 无单位的数值与行高2.6 自定义属性2.7 本章小结 2.3 告别…

安卓常用的控件

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 在Android开发中,控件(也称为视图或控件组件)是构建用户界面的基本元素。它们…

设计模式-代理模式和装饰者模式

二者都是结构型的设计模式. 1.代理模式 1.1定义 为其他对象提供一种代理以控制对这个对象的访问. 代理从code实现方面分为静态代理和动态代理两种; 从适用范围来看,分为远程代理,虚拟代理,保护代理,智能引用几种. 远程代理:为某个对象在不同的内存地址空间提供…

Esxi硬件日志告警

原创作者:运维工程师 谢晋 Esxi硬件日志告警 故障描述故障处理 故障描述 主机报错硬件对象状态告警 在Esxi监控硬件内发现Systemctl Manager Module 1 Event log 0报警,该报警是Esxi事件日志保存空间满了,需要清理空间。 故障处理 开启…

实现第一个神经网络

PyTorch 包含创建和实现神经网络的特殊功能。在本节实验中,将创建一个简单的神经网络,其中一个隐藏层开发一个输出单元。 通过以下步骤使用 PyTorch 实现第一个神经网络。 第1步 首先,需要使用以下命令导入 PyTorch 库。 In [1]: import…

Android super.img结构及解包和重新组包

Android super.img结构及解包和重新组包 从Android10版本开始,Android系统使用动态分区,system、vendor、 odm等都包含在super.img里面,编译后的最终镜像不再有这些单独的 image,取而代之的是一个总的 super.img. 1. 基础知识 …