文件操作和IO


认识文件

文件在计算机中,使保存到 硬盘 上的
硬盘通过 文件资源管理器 来管理文件
通过类似于“树形结构”管理所有文件的
通过“文件路径”的概念 描述一个文件
从根节点一直到该文件,过程中遇到的每一个目录的集合就是 文件路径
文件路径中的每一级目录 都是用 \ 来分割(Windows独有的设定,其他操作系统都是用 / )
例如:D:\游戏\aa\bbb

路径

以盘符开头的路径,也称为“绝对路径

和绝对路径并列,还有一种表示方式叫 相对路径【通常是以 . 或者 . . 开头的路径】
例如 ./bbb
谈到 相对路径 一定要明确相对路径的 “参考系” 是什么(即相对哪个目录【工作目录\基准目录】,以哪个目录为起点来进行 “相对” 的)

例如:
绝对路径为 D:\游戏\aa\bbb
设基准路径是 D:\游戏\aa ,则相对路径为 ./bbb
光靠相对路径是无法定位文件的,要结合基准路径才可以锁定文件的具体位置

定位同一个文件,如果基准路径不同,那么相对路径也不同
如:
绝对路径为 D:\游戏\aa\bbb
设基准路径是 D:\游戏,则相对路径为 ./aa/bbb
基准路径 + 相对路径 = 绝对路径

. ./ 的使用
假设有这样两个文件路径:
D:\游戏\Epic Games\DirectXRedist\sugarsalt
D:\游戏\Epic Games\DirectXRedist\aaa
想要通过 D:\游戏\Epic Games\DirectXRedist\aaa 找到 sugarsalt 文件,可以用 . ./sugarsalt
. ./ 表示当前路径的上级目录,在上面的示例中就相当于 略过了 \aaa 从 D:\游戏\Epic Games\DirectXRedist 往后找文件

D:\游戏\Epic Games\DirectXRedist\sugarsalt
D:\游戏\Epic Games\DirectXRedist\aaa\bbb 还是要通过下面的路径找上面的 sugarsalt
用 . ./. ./sugarsalt

文件的种类

所有的文件 分为两个大类:

  1. 文本文件
    文本文件存的都是 字符串,都是“合法的字符”【能在 utf-8 查到编码规则的】(.txt;.java;.c 都是文本文件)
  2. 二进制文件
    二进制文件存啥都行

简单判断一个文件是否是文本文件:
把该文件拖动到记事本中,如果没变成乱码,就是文本文件,否则是二进制文件

Java中操作文件

文件系统操作

创建文件,删除文件,移动文件,获取文件属性…

File 类

构造File 对象,核心就是指定 “路径”(绝对路径,相对路径)

  1. 构造方法
File (File parent, String child)
 根据⽗⽬录 + 孩⼦⽂件路径,创建⼀个新的 File 实例
 
File(String pathname)
 根据⽂件路径创建⼀个新的 File 实例,路径可以是绝对路径或者相对路径
 
File(String parent, String child)
 根据父目录+孩子文件路径,创建一个新的File实例,父目录用路径表示

使用举例:

File file=new File("d:/hello.txt");//绝对路径创建
File file1=new File("./hello.txt");//相对路径创建

相对路径在ideal 这类开发工具来说,能看到基准路径,例如在ideal 点击快捷键 ait+F12
在这里插入图片描述
补充:
PS 全称为 Power Shell 是Windows上的终端的名字

  1. 方法
修饰符及返回值类型   方法签名               说明
String              getParent()           返回 File 对象的父目录文件路径
String              getName()             返回 FIlle 对象的纯文件名称
String              getPath()             返回 File 对象的文件路径
String              getAbsolutePath()     返回 File 对象的绝对路径
String              getCanonicalPath()    返回File对象的修饰过的绝对路径
boolean             exists()              判断 File 对象描述的文件是否直实存在
boolean             isDirectory()         判断 File 对象代表的文件是否是一个目录
boolean             isFile()              判断 File 对象代表的文件是否是个普通文件
boolean             createNewFile()       根据 File 对象,自动创建一个空文件。成功创建后返回 true
boolean             delete()              根据 File 对象,删除该文件。成功删除后返回 true
void                deleteOnExit()        根据 File对象,标注文件将被册除,删除动作会到 JVM 运行结束时才会进行
String[]            list()                返回 File 对象代表的目录下的所有文件名
File[]              listFiles()           返回 File对象代表的目录下的所有文件,以File 对象表示
boolean             mkdir()               创建 File 对象代表的目录
boolean             mkdirs()              创建 File 对象代表的目录,如果必要,会创建中间目录
boolean             renameTo(File dest)   进行文件改名,也可以视为我们平时的剪切、粘贴操作
boolean             canRead()             判断用户是否对文件有可读权限
boolean             canWrite()            判断用户是否对文件有可写权限

代码示例

在这里插入图片描述
在这里插入图片描述

首先我的D盘里面有hello.txt文件
在这里插入图片描述
而相对路径没有创建hello.txt 文件
在这里插入图片描述

  1. 普通⽂件的创建、删除在这里插入图片描述
    在这里插入图片描述
  2. list() 和 listFiles()
    这两个都是针对目录操作,对文件没什么用
    在这里插入图片描述
    在这里插入图片描述
  3. mkdir() 和 mkdirs()
    mkdir() 支持创建一层目录,mkdirs() 支持创建多层目录

mk==>make
dir==>directory
在这里插入图片描述
在这里插入图片描述

  1. renameTo
    在这里插入图片描述
    renameTo 不光能重命名,还能切换位置
    在这里插入图片描述

文件内容操作(读写)–数据流

1.读文件:硬盘上的数据读到内存
2.写文件:把内存数据写到硬盘

基本使用逻辑:

  1. 打开
  2. 读/写
  3. 关闭

可以使用流对象进行文件内容操作(流——Stream)
可以把数据简单理解为水池中的水,写文件相当于往里面滴水【输出流】,读文件相当于往外流水【输入流】

Java通过一系列的类来表示“流对象”

  1. 字节流【读写数据的时候,以字节为基本单位,一次最少读写一个字节】
    InputStream —— 读
    OutputStream —— 写
  2. 字符流【读写数据的时候,以字符为基本单位,一次最少读写一个字符】
    Reader ——读
    Writer —— 写

(一个字符可能对应多个字节,主要看编码方式)
读写二进制文件,通常使用字节流
读写文本文件,通常使用字符流

InputStream 概述

InputStream 是一个抽象类,不能创建实例,所以我们可以实例化它的子类FileInputStream
(字节内容更习惯采用十六进制表示)
方法

修饰符及返回值类型       ⽅法名                            说明
   int                   read()                       一次读取一个字节,返回 -1 代表已经完全读完了
   
   int               read(byte[] b)                   一个参数版本,尝试把这个参数中的字节数组填满
   
   int            read(byte[] b, int off, int len)    最多读取 len - off 字节的数据到 b中,从 off 开
                                                      始,返回实际读到的数量;-1 代表以及读完了

FileInputStream

代码示例

在这里插入图片描述

在这里插入图片描述
此处的 bytes 参数,称为”输出型参数”
事先构造好一个空的数组 (不是 null, 里面的元素全0的数组)
由read 方法内部, 往参数数组中进行填充

Java 中一个方法,只能返回一个值
如果需要返回多个值,要么借助输出型参数,要么把返回的多个值打包成一个类
所以要把 bytes 的返回值打包给 read
read 会尽可能把 bytes 填满

在这里插入图片描述

注意:
使用完了这个文件一定要关闭文件

原因:进程 PCB 中有一个属性,文件描述符表 (顺序表)
每次打开一个文件,就会在这个表里插入一个元素
文件描述符表,长度是定长的(不能自动扩容)
光打开文件,不关闭文件,此时就会逐渐把这个表里的内容消耗殆尽;后续再尝试打开,就会打开失败
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1435e3e77d774066862c9112d94befae.png
在这里插入图片描述

关闭文件更简单的方法:try with resource
此处的 try 括号中可以打开多个流对象,用分号隔开

如:
try(File1 ; File2)

 try(InputStream inputStream=new FileInputStream("./hello.txt")){
            while (true){
                byte[] bytes=new byte[1024];
                int c=inputStream.read(bytes);
                if(c==-1)
                    break;
                for (int i = 0; i < c; i++) {
                    System.out.printf("0x%x\n",bytes[i]);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

OutputStream 概述

方法:

修饰符及返回值类型     ⽅法名                               说明
  void               write(int b)                     写⼊要给字节的数据
  
  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(刷新)操
                                                      作,将数据刷到设备中。

代码示例:
在这里插入图片描述
在这里插入图片描述
发现每次写文件后原来的内容被清空了每次按照写文件的方式打开文件,就会清空文件的原有内容
如果想要保存原有内容,可以在new文件的时候往后加一个 true
在这里插入图片描述
这个表示“追加写”
不会清空原有内容,新写入的内容就会从文件末尾继续追加

Reader 和 Writer

与InputStream 用法类似,只是它返回的是字符
在这里插入图片描述

代码示例:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

小程序练习

1.通过用户输入目录和文件名找到想搜索的文件位置

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

public class learn1 {
    public static void main(String[] args) {
        System.out.println("请输入你想搜索的目录:");
        Scanner scanner=new Scanner(System.in);
        String Dir=scanner.next();
        System.out.println("输入你想搜索的文件名:");
        String fileName=scanner.next();
        File rootDir=new File(Dir);
        if(!rootDir.isDirectory()){
            System.out.println("目录不存在!");
        }else {
            scanDir(rootDir,fileName);//递归查找文件
        }
    }
    public static void scanDir(File rootDir, String fileName){
        File[] files=rootDir.listFiles();
        if(files==null){
            return;//文件找到底,递归结束
        }
        for (File file:files){
            if(file.isDirectory()){//还是目录,再次递归
                scanDir(file,fileName);
            }else if(file.isFile()){
                if(file.getName().contains(fileName)){//找到了
                    isDelete(file);
                }
            }
        }
    }
    public static void isDelete(File file){
        System.out.println("找到了,是否删除(Y/N)");
        System.out.println(file.getAbsolutePath());
        Scanner scanner=new Scanner(System.in);
        String s=scanner.next();
        System.out.println();
        if(s.equals("Y") || s.equals("y")){
            file.delete();
            System.out.println("删除成功");
        }
    }
}

2.普通文件的复制

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

public class learn2 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("清输入源文件:");
        String file=scanner.nextLine();
        System.out.println("请输入目标文件:");
        String file1=scanner.nextLine();
        File srcfile=new File(file);
        File desFile=new File(file1);
        if(!srcfile.isFile()){
            System.out.println("该文件不存在!");
            return;
        }
        File parentFile=desFile.getParentFile();
        if(!parentFile.isDirectory()){
            System.out.println("目标文件的父文件不存在!");
            return;
        }
        copy(srcfile,desFile);//复制文件,进行读写操作
    }
    public static void copy(File srcFile,File desFile){
        try(InputStream inputStream=new FileInputStream(srcFile);
            OutputStream outputStream=new FileOutputStream(desFile)){
            while (true){
                byte[] bytes=new byte[1024];
                int c=inputStream.read(bytes);
                if(c==-1){
                    break;
                }
                outputStream.write(bytes,0,c);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值