JavaEE:文件IO

news2024/11/18 23:41:46

硬盘

文件指的是硬盘/磁盘上的文件

⚠硬盘 ≠ 磁盘

磁盘属于外存的一种;而软盘,硬盘(机械硬盘)这种属于用磁性介质来存储二进制数据

ssd硬盘(固态硬盘),内部完全是集成电路,和磁性介质无关。相比于机械硬盘,固态硬盘读写速度快了10倍。为什么?

下面是机械硬盘的构造

存储原理:磁头悬停在盘片的不同位置就能感知到磁性数据信息

对于机械硬盘来说,更擅长顺序读写,不擅长随机读写。

顺序读写:比如说有一个很大的文件,对这个文件整体复制一下。整个大文件就在盘片的某一个位置,磁头就在该位置进行数据感知,不需要太多的移动,速度就会加快。

随机读写:某个目录中有很多小文件(在硬盘上的存储不是连续的,是离散的,会出现在硬盘的各个位置),需要把整个目录都拷贝一下。此时磁头就需要不断变换位置去找文件,速度就会变慢。


计算机中的文件

文件是啥?

对于计算机来说,文件是一个广义的概念

1.硬盘上的普通文件

2.硬盘上的目录

3.很多硬件设备被操作系统抽象成了文件。比如键盘,显示器,网卡等


文件路径 

操作系统有一个专门的模块--文件系统,可以把硬盘希捷封装好,提供统一的api供我们调用

操作系统使用路径来描述一个具体文件的位置

比如:"C:\Users\86156\Desktop\大二下\Coursera-ML-AndrewNg-Notes-master\Coursera-ML-AndrewNg-Notes-master\README.md"这个路径

Windows从盘符出发,一级一级往下走,走到目标文件,把中间经过的目录都串起来,使用/或者\进行分割。注意哦,这可不是遍历,遍历是要把文件树上每一个文件都不重不漏地遍历一遍

什么样地情况才算遍历?当我们直接搜索这个文件名的时候

Everything软件:一款秒搜Windows系统上文件的工具

核心思路:空间换时间

Everything里内置了一个数据库,在最初安装好的时候,软件会对整个文件系统进行一个遍历并把信息都放到内置数据库中,后面搜索的时候就直接在内置数据库里查询

Everything可以感知硬盘上的文件变化,并更新自己的数据库

路径有两种风格

1.绝对路径。从树的根节点出发(Windows是盘符),一层一层到达目标文件

2.相对路径。先指定一个“当前目录”/“工作目录”/“基准目录”,从当前目录出发,找到目标文件

举例:

1.当前目录 C:\Users\1,则相对路径是 ./test.txt(此时的 . 表示当前目录)

2.当前目录 C:\Users\1\AppData,则相对路径 ../test.txt(此时的 .. 表示上层目录)

\ 和 / 是怎么回事?

操作系统中分割目录的各个部分,正统使用 /

由于微软早期做了一个名为DOS的系统,同时支持用 \ 和 / 来进行分割,Windows也继承了这一特性。Linux系统基本采用 / 


文件类型

文本文件:按照文本/字符串方式来理解文件内容,文本文件的二进制内容表示的是字符串;文本文档,.c,.java,.cpp都是文本文件

二进制文件:内容存储任何数据都可以(打开来有乱码的就是二进制文件);图片,音频,视频,可执行程序,动态库,.class都是二进制文件

富文本文件:比普通文件丰富的多,比如.docx,.pptx,.xlsx


Java对于文件操作的API

1.针对文件系统的操作(右键文件目录能进行的操作)

在java.io包中的File类中进行

io指的是输入和输出 (⚠输入指的是数据从硬盘到CPU的过程,输出指的是数据从CPU到硬盘的过程)


File类里面的方法


代码演示

绝对路径

        File f = new File("C:/Users/1/test.txt");
        System.out.println(f.getParent());
        System.out.println(f.getName());
        System.out.println(f.getPath());
        System.out.println(f.getAbsolutePath());
        System.out.println(f.getCanonicalFile());

 


相对路径

        File f = new File("./test.txt");
        System.out.println(f.getParent());
        System.out.println(f.getName());
        System.out.println(f.getPath());
        System.out.println(f.getAbsolutePath());
        System.out.println(f.getCanonicalFile());//这个路径就是针对上面的绝对路径整理化简之后的效果了


创建文件

  isDirectory 是否是文件夹

第二次执行,因为文件已经存在了,所以会创建失败


删除文件

第二次执行

deleteOnExit 退出时再删除

退出后删除的文件也叫临时文件,给你实时编辑的内容进行保存,防止你文件没有保存电脑突然断电的情况(比如word等程序)


文件列表

System.out.println(Arrays.toString(files));

创建目录

mkdir: mk(make)   dir(directory)

需要在构造方法中把路径创建好,再通过mkdir创建

文件重命名

执行前

执行后

renameTo还可以移动文件


2.针对文件内容的操作,读文件/写文件

流:操作系统提供的概念

像接水一样,有100ml,可以一次接10ml,接10次;一次接5ml,接20次。怎么接结果都一样

文件流也一样,比如要读写100字节的数据,可以一次读写10字节,分10次;也可以一次读写5字节,分20次;此处的读写方式是任意多的情况,最终的效果是一样的

1.字节流

打开/关闭文件

以字节为单位进行读写,一次最少读写一个字节

InputStream和OutputStream

InputStream是一个抽象类,无法被实例化,所以不能直接new

但是我们可以使用InputStream其中一个子类进行实例化

这个表示从文件中读取,()内填写文件的路径,创建对象的同时也打开了文件

这里的关闭文件可以理解为释放了文件的相关资源

为什么要关闭文件?(文件资源泄露问题)

文件描述符表记录了当前进程都打开了哪些文件,这个表类似顺序表或者数组,数组中的每个元素都是一个结构体,这个结构体具体描述了了这个文件的一些属性。

因为每次打开一个文件都会在文件描述符表占据一个位置,如果长期不关闭文件就会使文件描述符表空间被耗尽(⚠文件描述符表无法自动扩容,因为操作系统内核任务重,对性能要求高,如果引起卡顿后果无法设想)

如果空间被耗尽,后面的文件就无法打开

上面的代码有点问题:如果在打开文件后写入的逻辑里面出现return,close指令就无法执行了

修改方法:用try和finally来包裹

        //因为try里面定义的inputStream finally里面访问不到,所以我们把它定义到外面
        InputStream inputStream = null;
        try{
            //打开文件
            inputStream = new FileInputStream("./test.txt");
            //写入其他逻辑

        }finally{
            //关闭文件
            inputStream.close();
        }
        //或者写成下面这样
        try(InputStream inputStream = new FileInputStream("./test.txt")){

        }

 !!第二种写法的代码一旦出来try代码块,会自动执行close方法


读取文件

read() 无参数版本,每次调用读取一个字节,返回读取这个字节的值

返回类型是int,实际上是byte,取值范围是0~255. 有一个特殊情况,如果读取到文件末尾,继续进行read就会返回-1,所以还是用int表示. 

read(byte[ ] b) 一个参数版本,传入的字节数组参数是一个输出型参数

byte[ ]是引用类型,使用read的时候,往往就是定义一个内容为空的数组,然后方法内部针对数组内容进行修改,方法执行结束之后在修改后的数组在方法外部也能生效

read(byte[ ] b, int off, int len)  off是偏移量,len表示实际读取的字节个数

测试代码(先在文件目录里面创建一个test.txt,写上abcdef

        try(InputStream inputStream = new FileInputStream("./test.txt")){
            while (true){
                int b = inputStream.read();
                if(b == -1){
                    //文件读取完毕
                    break;
                }
                //不等于-1.打印这个字节的数据
                System.out.printf("%x ",b);
            }
        }

执行代码

打印的内容就是文件中每个字节的数据,也就是abcdef的ASCII码(注意16进制打印ASCII的跟10进制的值不一样)

如果文件带有中文,则打印出的每一个字节就对应到utf8编码的值


一次读若干字节,比一次读一个字节来的高效(都硬盘是低效操作,而访问内存是高效操作,如果可以每次把字节攒进内存一次性从硬盘中读取,就会高效很多)

                byte[] buffer = new byte[1024];
                int n = inputStream.read(buffer);
                if(n == -1){
                    //文件读取完毕
                    break;
                }
                for (int i = 0; i < n; i++) {
                    System.out.printf("%x ",buffer[i]);

2.字符流

以字符为单位进行读写。如果是utf8来表示汉字,3个字节就是一个汉字,每次读写都得一3个字节为单位来进行读写,不能一次读写半个汉字

写入文件 

第一个:一次写一个字节;

第二个:一次写若干字节--写一整个数组

第三个:写数组的一部分

OutputStream在默认情况下,会把之前文件内容都清空掉,然后再重新开始写

我在刚创建的test.txt里面写入“你好”

,然后执行下面的代码

    public static void main(String[] args) {
        //清空操作是下面这行try进行的
        try(OutputStream outputStream = new FileOutputStream("./test.txt")){
            byte[] buffer = new byte[]{97,98,99,100,101,102};//;abcdef
            outputStream.write(buffer);
        }  catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

执行结果

原来的“你好”没了,变成了abcdef

也可以不用清空原文件的内容,在原来文件的内容上追加写东西


Reader和Writer字符流

Reader的read方法(按照char为单位)

问题:char占2个字节,读取中文的时候,每个字符是3个字节的utf8,但是这里读出来的每个字符为何都变成2个字节了

此处这个代码相当于把当前文件中的utf8按照字符读取并转成Unicode,每个char[ ]数组里面存储的是对应的Unicode的值

在这个代码就把char[ ]数组转换成String utf8的形式,这个转换过程是Java已经封装好了,无法直接感知到的

Writer的write方法

基本代码模式


3.查找硬盘上的文件

给定一个文件名,去指定的目录进行搜索,找到文件名匹配的结果,并打印出完整的路径

因为文件系统目录是树形结构,所以我们需要用到递归来遍历树

注意:这里的遍历不是前,中,后序其中任何一种,因为此处是N叉树,每个节点上有很多文件

    public static void main(String[] args) {
        //1.输入必要的信息
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要搜索的文件名");
        String fileName = scanner.next();
        System.out.println("请输入要搜索的目录");
        String rootPath = scanner.next();
        File rootFile = new File(rootPath);
        if(!rootFile.isDirectory()){
            System.out.println("输入的路径有误");
            return;
        }
        //2.有了路径之后就可以按照递归的方式来搜索
        //知道递归的起点还需要知道要查询的文件名
        scanDir(rootFile, fileName);
    }
    private static void scanDir(File rootFile, String fileName){
        //1.把当前目录中的文件和目录都列出来
        File[] files = rootFile.listFiles();
        if(files == null){
            //空的目录就直接返回
            return;
        }
        //2.遍历上述files,判定每一个file是目录还是文件
        for(File f : files){
            if(f.isFile()){
                //普通文件,判定文件名是否是搜索的文件
                if(fileName.equals(f.getName())){
                    System.out.println("找到了符合要求的文件! "+f.getAbsolutePath());
                }
            } else if (f.isDirectory()) {
                //目录文件,需要进一步的递归
                scanDir(f, fileName);
            }else{
                ;
            }
        }
    }

代码中判定文件名的操作和递归操作是混着来的,所以也解释了前中后序啥都不是的原因


4.复制文件

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要复制的原文件:");
        String srcPath = scanner.next();
        System.out.println("请输入要复制的目标文件:");
        String destPath = scanner.next();

        //文件合法性判定
        //1)srcPath对应的文件是否存在
        File srcFile = new File(srcPath);
        if(!srcFile.isFile()){
            System.out.println("源文件路径有误");
            return;
        }
        //2)destPath 不要求对应的文件存在,但是目录得存在
        File destFile = new File(destPath);
        if(!destFile.getParentFile().isDirectory()){
            System.out.println("目标路径有误!");
            return;
        }
        //进行复制操作
        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){
                    break;
                }
                //把读到的内容写道outputStream中
                outputStream.write(buffer, 0, n);
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }

创建文件

在a1.txt里面写入120;执行程序,程序成功复制了文件


5.进阶搜索

在目录中搜索,按照文件内容搜索,用户输入目录名称和一个要搜索的词。如果在搜索过程中找到这个词就返回整个文件的路径

package io;

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

public class IODemo15 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要搜索的路径: ");
        String rootPath = scanner.next();
        System.out.println("请输入要查询的词: ");
        String word = scanner.next();

        File rootFile = new File(rootPath);
        if (!rootFile.isDirectory()) {
            System.out.println("输入的要搜索的路径不正确!");
            return;
        }

        scanDir(rootFile, word);
    }

    private static void scanDir(File rootFile, String word) {
        File[] files = rootFile.listFiles();
        if (files == null) {
            return;
        }
        for (File f : files) {
            System.out.println("当前遍历到: " + f.getAbsolutePath());
            if (f.isFile()) {
                // 在文件内容中搜索
                searchInFile(f, word);
            } else if (f.isDirectory()) {
                // 递归遍历
                scanDir(f, word);
            } else {
                // 暂时不需要
                ;
            }
        }
    }

    private static void searchInFile(File f, String word) {
        // 通过这个方法在文件内部进行搜索
        // 1. 把文件内容都读取出来.
        try (InputStream inputStream = new FileInputStream(f)) {
            StringBuilder stringBuilder = new StringBuilder();
            while (true) {
                byte[] buffer = new byte[1024];
                int n = inputStream.read(buffer);
                if (n == -1) {
                    break;
                }
                // 此处只是读取出文件的一部分. 需要把文件内容整体拼接在一起.
                String s = new String(buffer, 0, n);
                stringBuilder.append(s);
            }

            // 加了打印之后, 可以看到, 文件内容是对的. 说明后面的匹配有问题.
            System.out.println("[debug] 文件内容: " + stringBuilder);

            // 当文件读取完毕, 循环结束之后, 此时 stringBuilder 就是包含文件整个内容的字符串了.
            if (stringBuilder.indexOf(word) == -1) {
                // 没找到要返回.
                return;
            }
            // 找到了, 打印文件的路径
            System.out.println("找到了! " + word + " 存在于 " + f.getAbsolutePath());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

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

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

相关文章

手写简易操作系统(七)--加载操作系统内核

前情提要 上一节中&#xff0c;我们开启了内存分页&#xff0c;这一节中&#xff0c;我们将加载内核&#xff0c;内核是用C语言写的&#xff0c;C语言编译完了是一段ELF可加载程序&#xff0c;所以我们需要学会解析ELF格式文件&#xff0c;并将内核加载到内存 一、ELF格式 程…

(黑马出品_高级篇_04)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_高级篇_04&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——可靠性消息服务 今日目标服务异步通信-高级篇1.消息可靠性1.1.生产者消息确认1.1.1.修改配置1.1.2.定义Return回调1.1.3.定义ConfirmCallbac…

【LeetCode热题100】2. 两数相加(链表)

一.题目要求 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数…

论文阅读——RemoteCLIP

RemoteCLIP: A Vision Language Foundation Model for Remote Sensing 摘要——通用基础模型在人工智能领域变得越来越重要。虽然自监督学习&#xff08;SSL&#xff09;和掩蔽图像建模&#xff08;MIM&#xff09;在构建此类遥感基础模型方面取得了有希望的结果&#xff0c;但…

深度学习系列62:Agent入门

1 anget介绍和openai标准接口 agent的核心是其代理协同工作的能力。每个代理都有其特定的能力和角色&#xff0c;你需要定义代理之间的互动行为&#xff0c;即当一个代理从另一个代理收到消息时该如何回复。 agent目前大多使用openai标准接口调用LLM服务&#xff0c;说明如下。…

Java集合基础知识总结(绝对经典)

List接口继承了Collection接口&#xff0c;定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理&#xff0c;还添加了面向位置的操作。 实际上有两种list&#xff1a;一种是基本的ArrayList&#xff0c;其优点在于随机访问元素&#xff0c;另一种是更强大的L…

PYTHON 自动化办公:更改图片后缀

1、前言 在之前的文章中&#xff0c;介绍了图片的压缩技术&#xff0c;这里讲解如何利用python批量将图片改为指定后缀的格式。当然&#xff0c;也可以为深度学习批量更改文件后缀&#xff0c;例如在分割中&#xff0c;可能需要img和mask的图片名称完全一致等等 PYTHON 自动化…

掌握FilterOutputStream类!

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java IO相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

谈谈Darknet53为啥这么难训练

在我使用Imagenet2012对Darknet53进行预训练的时候&#xff0c;往往训练到一半&#xff0c;就会出现过拟合&#xff0c;导致无法继续向下训练&#xff0c;尝试了很多方法&#xff0c;最后发现问题出现在下图红框的部分。 得出这个结论是因为当我使用Resnet中&#xff0c;包含有…

力扣654 最大二叉树 Java版本

文章目录 题目描述解题思路代码 题目描述 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上…

【DVWA】19. Insecure CAPTCHA 不安全的验证码(全等级)

文章目录 1. Low1) 源码分析2&#xff09;实操 2. Medium1) 源码分析2&#xff09;实操 3. High1) 源码分析2&#xff09;实操 4. Impossible1) 源码分析 1. Low 1) 源码分析 <?phpif( isset( $_POST[ Change ] ) && ( $_POST[ step ] 1 ) ) {// Hide the CAPTC…

WPF图表库LiveCharts的使用

这个LiveCharts非常考究版本&#xff0c;它有非常多个版本&#xff0c;.net6对应的是LiveChart2 我这里的wpf项目是.net6&#xff0c;所以安装的是这三个&#xff0c;搜索的时候要将按钮“包括愈发行版”打勾 git&#xff1a;https://github.com/beto-rodriguez/LiveCharts2?…

BUGKU-WEB never_give_up

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 解题思路 F12查看请求和响应&#xff0c;查找线索 相关工具 base64解码URL解码Burp Suit抓包 解题步骤 F12查看请求和响应&#xff0c;发现一行注释包含一个文件名称【1p.html】&#xff0c;这应该就是提…

GaN HEMTs在电力电子应用中的交叉耦合与基板电容分析与建模

来源&#xff1a;Analysis and Modeling of Cross-Coupling and Substrate Capacitances in GaN HEMTs for Power-Electronic Applications&#xff08; TED 17年&#xff09; 摘要 本文提出了一种考虑了基板电容与场板之间交叉耦合效应的场板AlGaN/GaN高电子迁移率晶体管(HE…

RabbitMQ自学笔记——消息可靠性问题

1.发送者的可靠性 1.1生产者重连 有时由于网络波动等原因&#xff0c;发送方一次可能没有连接上RabbitMQ&#xff0c;我们可以配置发送方的连接失败重试机制。但需要注意的是&#xff1a;SpringAMQP提供的重试机制是阻塞式的重试&#xff0c;也就是说多次重试等待的过程中&am…

[JAVAEE]—进程和多线程的认识

文章目录 什么是线程什么是进程进程的组成什么是pcb 进程概括线程线程与进程的关系线程的特点 创建线程创建线程方法创建线程的第二种方法对比 其他的方式匿名内部类创建线程匿名内部类创建Runable的子类lambda表达式创建一个线程 多线程的优势 什么是线程 什么是进程 首先想…

iOS 判断触摸位置是否在图片的透明区域

装扮功能系列&#xff1a; Swift 使用UIScrollerView 实现装扮功能&#xff08;基础&#xff09;Swift 使用UIScrollerView 实现装扮功能&#xff08;拓展&#xff09;iOS 判断触摸位置是否在图片的透明区域 背景 在装扮功能中&#xff0c;一般都是长按使道具进入编辑状态&…

ES分布式搜索-使用RestClient操作索引库

RestClient操作索引库 1、什么是RestClient&#xff1f; ES官方提供了各种不同语言的客户端&#xff0c;用来操作ES。这些客户端的本质就是组装DSL语句&#xff0c;通过http请求发送给ES。官方文档地址&#xff1a;Elasticsearch Clients官方文档 2、利用JavaRestClient实现…

软考高级:软件架构风格-闭环控制概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Python 查找PDF中的指定文本并高亮显示

在处理大量PDF文档时&#xff0c;有时我们需要快速找到特定的文本信息。本文将提供以下三个Python示例来帮助你在PDF文件中快速查找并高亮指定的文本。 查找并高亮PDF中所有的指定文本查找并高亮PDF某个区域内的指定文本使用正则表达式搜索指定文本并高亮 本文将用到国产第三方…