详解文件IO操作

目录

1. 基础知识

文件操作

2. File类

3. 流(针对文件内容操作读写)

3.1 InputStream 概述

3.2 Scanner

4. 代码小练习


1. 基础知识

(1)狭义上的文件就是,存储在硬盘上的数据,这些以“文件”为单位,组织在一起

常见的文件(文本文件、图片、视频、音频、可执行程序...)

文件夹也叫做“目录”,这也是一种特殊的文件

我们在电脑文件资源管理器中,随便打开一个,可以看到这些就是狭义上的文件

 (2)广义上的文件就是,操作系统负责管理软硬件资源,操作系统(Linux)一般都会将这些资源都统一抽象为“文件”来进行管理。

比如,电脑上的键盘,电脑想从键盘读取数据,就是把键盘抽象为一个文件,读这个文件就能读到用户输入的案件内容。

再比如,电脑上的音响,电脑想给音响发送数据播放音乐,就是把音响抽象为一个文件,写这个文件就可以让音响播放音乐

本篇所写的文件操作就是,指狭义上的文件

(3)描述电脑上一个文件的具体位置,通过“路径”来描述,有两种

a)绝对路径:      D:\腾讯软件\QQ音乐\QQMusic\QQMusicUp.exe

目录之间的分割符,可以使用 \ (反斜杠) ,也可以使用 / (斜杠)

但是我们在写代码时,最好使用 / (斜杠),来表示路径

b)相对路径:  写相对路径,必须要知道基准路径是什么

 需要注意,如果使用当前的基准路径找不到QQMusicUp.exe

 那就可以使用 ..返回到上级目录(..表示当前路径的上级路径)

当前是:     D:\腾讯软件\腾讯会议\WeMeet

第一次使用..    返回到   D:\腾讯软件\腾讯会议

第二次使用..    返回到   D:\腾讯软件

这样就可以在 D:\腾讯软件 中往下找了


文件操作

文件操作,是属于操作系统层面,提供的一些API

不同的操作系统,提供的API是不一样的

而Java就在JVM中把不同系统的操作文件的API进行封装了,这样我们就可以使用Java中的库的代码来操作文件了

2. File类

Java 中通过 java.io.File 类对一个文件(包括目录)进行操作
注意:有 File 对象,并不代表真实存在该文件。

注意下面的这操作,只是在操作“文件系统”(操作系统中管理文件的核心功能)

新增文件、删除文件、新增目录、列出目录内容。重命名、获取路径...

这些都不是针对文件内容的操作

具体FIle的构造和方法,大家可以翻看一下JDK帮助手册,没有的可以私信我给你发一份

demo1,看一下File的常用方法

public class demo01 {
    public static void main(String[] args) throws IOException {
//        File f = new File("D:/test.txt");
        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.getCanonicalPath());
    }
}

demo02, 判断文件是否存在,不存在就创建

public class demo02 {
    public static void main(String[] args) throws IOException {
        File f = new File("./test.txt");
        //判定存在
        System.out.println(f.exists());
        //判定是否是目录
        System.out.println(f.isDirectory());
        //判定是否是文件
        System.out.println(f.isFile());

        //把这个文件创建出来
        f.createNewFile();

        //判定存在
        System.out.println(f.exists());
        //判定是否是目录
        System.out.println(f.isDirectory());
        //判定是否是文件
        System.out.println(f.isFile());
    }
}

demo03,删除文件
public class demo03 {
    public static void main(String[] args) {
        File f = new File("./test.txt");
        f.delete();
    }
}

可以看到代码运行后,test.txt 删除成功

 还有一个是,deleteOnExit 进程退出时删除文件

demo04,创建目录

//创建目录
public class demo04 {
    public static void main(String[] args) {
            File f = new File("./testDir");
            f.mkdir();
    }
}

运行代码后,可以看到目录创建成功

 注意 mkdir 是创建单层目录            mkdirs 是创建多层目录

public class demo04 {
    public static void main(String[] args) {
          File f1 = new File("./testDir/aaa/bbb");
          f1.mkdirs();
    }
}

demo05,重命名

public class demo05 {
    //重命名
    public static void main(String[] args) {
        File srcFile = new File("aaa.txt");
        File destFile = new File("bbb.txt");
        destFile.renameTo(destFile);
    }
}

程序运行前,程序运行后,

demo06,返回 File 对象代表的目录下的所有文件名

public class demo06 {
    public static void main(String[] args) {
        File f = new File("./testDir");
        String[] results = f.list();
        System.out.println(Arrays.toString(results));
    }
}

 


3. 流(针对文件内容操作读写)

3.1 InputStream 概述

Java标准库,在“流”的概念上,提供了一组类,完成读写文件的操作
这组类,分为两种( 这两种都是抽象类,不能实例化)
a)字节流(以字节为基本单位),适用于二进制文件
InputStream 读         OutputStream 写
b)字符流(以字符为基本单位),适用于文本文件
Reader 读          Writer 写
上面的这些都是 父类,
在标准库中,给这些父类还提供了各种子类的实现,以适应各种不同常见下的读写需求
InputStream --> FIleInputStream          OutputStream --> FileOutputStream
Reader --> FileReader                          Writer -->  FileWriter
InputStream
方法说明
int read()读取一个字节的数据,返回-1代表已经完全读完了
int read(byte[] b)最多读取b.length字节的数据到b中,返回实际读到的数量;-1代表已经读完了
int read(byte[] b, int off, int len)最多读取len - off 字节的数据到b中,放在从 off 开始,返回实际读到的数量;-1代表已经读完
void close()关闭字节流

int read(byte[] b) 和 int read(byte[] b, int off, int len) 是把读到的内容放到参数字节b数组中
参数b用来存放方法读取到的结果
需要注意正常情况下,对于方法来说,参数表示的是“输入信息”,返回值才是“输出信息”
但此时,是用参数b来作为存放输出结果的内容,b称为是“输出型参数”

(1)InputStream    read()读取文件中的字节数据

public class demo07 {
    public static void main(String[] args) throws IOException {
        //打开文件
        InputStream inputStream = new FileInputStream("./bbb.txt");
        while(true) {
            int b = inputStream.read();
            if(b == -1) {
                break;
            }
            System.out.println(b);
        }
        //关闭文件
        inputStream.close();
    }
}

(2)OutputStream    write()写文件

需要注意的是,使用OutputStream写文件时,只要打开文件成功,就会把文件原有内容清空,重新给进写

public class demo08 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("./bbb.txt");

        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);

        outputStream.close();
    }
}

(3)Reader  使用字符流读文件

 需要注意read读取到的是int类型,要用int类型来接收,最后打印时强转为char类型

//使用字符流读文件
public class demo09 {
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("./bbb.txt");
        while(true) {
            //把读到的内容用ret接收一下
            int ret = reader.read();
            //判断一下ret是不是-1如果是,那就是读完了
            if(ret == -1) {
                break;
            }
            //如果没有读完就再强转接收打印出来
            char ch = (char)ret;
            System.out.println(ch);
        }
        reader.close();
    }
}

(4)Writer  使用字符流来写文件

public class demo10 {
    public static void main(String[] args) throws IOException {
        //使用字符流来读文件
        Writer writer = new FileWriter("./bbb.txt");
        writer.write("hello world");
        writer.close();
    }
}

3.2 Scanner

针对文本文件,使用字符流的时候,还可以使用Scanner来进行读取

InputStream是字节流

Scanner 是在InputStream的基础上,包装的字符流

public class demo11 {
    public static void main(String[] args) throws IOException {
        //使用scanner  读取文本文件
        InputStream inputStream = new FileInputStream("./bbb.txt");

        //可以给Scanner的构造对象中传入一个 字符流对象
        Scanner scanner = new Scanner(inputStream);
        while(scanner.hasNext()) {
            System.out.println(scanner.next());
        }
        inputStream.close();
    }
}

使用PrintWriter可以简化写文件操作开发

public class demo12 {
    public static void main(String[] args) throws IOException {
        //针对写文件来说,还可以使用 PrintWriter 来简化开发
        OutputStream outputStream = new FileOutputStream("./bbb.txt");
        PrintWriter writer = new PrintWriter(outputStream);

        writer.println();
        writer.printf("a = %d\n", 10);

        outputStream.close();
    }
}

进行文件操作,一定要进行close(否则会造成文件资源泄露)

如果在代码中,close 前面的代码抛出异常了,后面的 close 就执行不到了

所以在写代码时,可以使用 try_finally来保证 close一定会执行到 

    public static void main(String[] args) throws IOException {
        //针对写文件来说,还可以使用 PrintWriter 来简化开发

        OutputStream outputStream = null;
        try{
            OutputStream outputStream1 = new FileOutputStream("./bbb.txt")
            PrintWriter writer = new PrintWriter(outputStream);
            
            writer.println();
            writer.printf("a = %d\n", 10);
        } finally {
            outputStream.close();
        }
}

也可以直接将OutputStream写进try中(try with resources  也就是带资源的try)

把要关闭的对象写到try的()中,当try结束,就会自动的调用到,对应对象的close方法

而且支持一个()放多个对象,多个对象的创建之间使用 封号 “;”分割就可以了

public class demo12 {
    public static void main(String[] args) throws IOException {
        //针对写文件来说,还可以使用 PrintWriter 来简化开发

        try(OutputStream outputStream = new FileOutputStream("./bbb.txt")){
            PrintWriter writer = new PrintWriter(outputStream);

            writer.println();
            writer.printf("a = %d\n", 10);
        } 
}
}

4. 代码小练习

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

a)先让用户输入:要扫描的路径+要查找的词

b)遍历目录,找到名字匹配的文件

这个需要用到一个核心方法:listFiles() 如果当前是一个目录,直接调用这个方法,就能把当前目录中包含的文件和子目录都能够列举出来

但是这个方法只能列举出一层,没法直接列出子目录中的子目录

解决的方法就是,遍历listFIles的结果,针对每一个元素,进行判定,看是一个普通文件,还是一个目录。(此处的目录结构本质上是一个“树”)

如果是普通文件,直接判定文件名包含了要查找的词,如果是目录,递归的调用listFiles

c)询问用户是否删除

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

public class demo01 {
    public static void main(String[] args) throws IOException {
        //1.让用户输入 要扫描的路径+要查找的关键词
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径:");
        File rootDir = new File(scanner.next());
        //用isDirectory判断一下rootDir是不是目录
        if(!rootDir.isDirectory()) {
            System.out.println("您输入的目录不存在!");
            return;
        }
        System.out.println("请输入要搜索的关键词:");
        String toDelete = scanner.next();

        //2.遍历目录,找到名字匹配的文件
        scanDir(rootDir, toDelete);
    }

    //借助这个方法进行递归遍历
    private static void scanDir(File rootDir, String toDelete) throws IOException {
        System.out.println("当前访问:" + rootDir.getCanonicalPath());
        File[] files = rootDir.listFiles();
        if(files == null) {
            //说明 rootDir是一个空的目录
            return;
        }
        //如果目录非空,则循环遍历每个元素
        for (File f : files) {
            if(f.isDirectory()) {
                scanDir(f, toDelete);
            }else {
                //不是目录,是普通文件,判断文件名是否是所要查找的,是否要进行删除
                checkDelete(f, toDelete);
            }
        }
    }

    private static void checkDelete(File f, String toDelete) throws IOException {
        //看一下这个 文件 中是否包含了 toDelete
        if(f.getName().contains(toDelete)) {
            System.out.println("这个关键词" + toDelete + "被" +
                    f.getCanonicalPath() + "包含了,是否要进行删除?(Y/N)");
            Scanner scanner = new Scanner(System.in);
            String choice = scanner.next();
            if(choice.equals("Y") || choice.equals("y")) {
                f.delete();
                System.out.println("删除成功!");
            }
        }
    }
}

(2)进行普通文件的复制

把第一个文件打开,把里面的内容逐个字节的读出来,写入到第二个文本中就可以了

使用字节流来进行操作,字节流也是可以用来拷贝文件的

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

//进行普通文件的复制
public class demo02 {
    public static void main(String[] args) {
        //1.先输入要复制哪个文件 和 将文件复制到哪里去(目标文件)
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入源文件:");
        //使用File的作用是可以判断文件是否存在
        File srcFile = new File(scanner.next());
        System.out.println("请输入目标文件:");
        File destFile = new File(scanner.next());
        if(srcFile.isFile()) {
            System.out.println("输入的源文件有误!");
            return;
        }
        //getParentFile()如果文件不存在就抛出异常
        if(!destFile.getParentFile().isDirectory()) {
            System.out.println("输入的目标文件有误!");
            return;
        }

        //2.打开源文件,按照字节读取内容,依次写入到目标文件中
        try(InputStream inputStream = new FileInputStream(srcFile);
        OutputStream outputStream = new FileOutputStream(destFile)) {
            //读 src 的每个字节,写入到 dest中
            while (true) {
                int ret = inputStream.read();
                //如果读到-1就结束
                if(ret == -1) {
                    break;
                }
                outputStream.write(ret);
            }
        }  catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(3)扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)

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

public class demo03 {
    public static void main(String[] args) throws IOException {
        //1.输入路径和要查询的关键词
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径:");
        File rootDir = new File(scanner.next());
        System.out.println("请输入要查询的关键词:");
        String toFind = scanner.next();

        //2.递归的进行扫描目录
        scanDir(rootDir, toFind);
    }

    private static void scanDir(File rootDir, String toFind) throws IOException {
        //listFiles将当前目录中的文件和子目录都列举出来
        File[] files = rootDir.listFiles();
        if(files == null) {
            return;
        }
        for (File f : files) {
            if(f.isDirectory()) {
                scanDir(f, toFind);
            } else {
              checkFile(f, toFind);
            }
        }
    }

    private static void checkFile(File f, String toFind) throws IOException {
        //1.先检查文件名
        if(f.getName().contains(toFind)) {
            System.out.println(f.getCanonicalPath() + " 文件名中包含 " + toFind);
        }
        //2.检查文件内容
        try(InputStream inputStream = new FileInputStream(f)) {
            StringBuilder stringBuilder = new StringBuilder();
            Scanner scanner = new Scanner(inputStream);
            //这里需要按行读取了
            while (scanner.hasNextLine()) {
                stringBuilder.append(scanner.nextLine() + "\n");
            }
            if(stringBuilder.indexOf(toFind) > -1) {
                System.out.println(f.getCanonicalPath() + " 文件内容包含 " + toFind);
            }
        }
    }
}

 

  • 41
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 31
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快到锅里来呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值