文件操作和IO

本文深入探讨了Java中的文件操作,包括文件的概念、分类、路径,以及如何使用File类进行创建、删除、重命名等操作。同时,讲解了字节流和字符流在读写文件中的应用,提供了丰富的示例代码,帮助开发者掌握文件内容的读写。此外,还分享了文件内容操作的实际案例,如文件复制和查找。
摘要由CSDN通过智能技术生成

👦有缘,才相遇,你好!我是hgway_hxz

❤️热爱Java,希望结识更多的小伙伴一起交流

🎉欢迎大家:👍点赞 💬评论 ⭐收藏 💖关注

✉️如果有任何问题欢迎大家在评论区讨论或者私信我

✏️命运因努力而精彩

1.认识文件

1.1 文件的概念

我们先来认识狭义上的文件(file)。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。在计算机中,文件可能是一个广义的概念,不仅包含普通文件,还包含目录(文件夹)。

1.2 文件的分类

站在程序员的角度来看,文件可以分成两类

  1. 文本文件:里面存储的是字符(本质上来说,文本文件也是存字节的,但是文本文件中,相邻的字节之间有联系刚好构成一个一个的字符)
  2. 二进制文件:里面存储的字节(相邻的字节之间没啥联系)

如何知道这个文件是文本文件还是二进制文件呢?

用记事本打开,把这个文件拖进来,如果是乱码的话,这个文件就是二进制文件,不是乱码就是文本文件。

比如我们的.txt,.java等属于文本文件,但.doc,.ppt,.xls等Office办公软件一般属于二进制文件,这种软件保存的不是一个单纯的文件,而是一个富文本,里面包含各种格式化的信息(字体大小,颜色……)。

1.3文件的路径

  • 绝对路径

绝对路径是以盘符开头的。例如D:\Program Files (x86)\Tencent\WeChat

  • 相对路径

相对路径是以.或者…开头的,其中**.表示当前路径**,…表示当前路径的上一级路径(父目录),但必须要有一个基准路径,相对路径就是从基准路径出发的。

例如:

有一个路径D:\Tencent\WeChat,其中D:\Tencent目录下有个text.txt文件

如果以D:\Tencent为基准路径,要找到text.txt文件,相对路径就是./text.txt,此处的.表示当前的路径(基准路径)。

如果以D:\Tencent\WeChat为基准路径,要找到text.txt文件,相对路径就是…/text.txt,此处的…表示基准路径的上一级路径D:\Tencent,然后再从这个路径中寻找text.txt这个文件。

2.文件系统的操作

我们的计算机就是通过“文件资源管理器”来进行文件的操作的

在这里插入图片描述

这里我们可以创建目录/文件,删除目录/文件,重命名文件……

在Java中就提供了一个File类来文件上述的操作,这个File类就描述了一个文件/目录,通过这个对象就可以实现这个功能。

2.1File类中的常见属性,构造方法和方法。

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String 类型的表示
static charpathSeparator依赖于系统的路径分隔符,char 类型的表示

构造方法

签名说明
File(File parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者 相对路径
File(String parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用 路径表示

方法

修饰符及返回 值类型方法签名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返 回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象 表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目 录
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操 作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

以一个参数的构造方法为例

示例一

public static void main(String[] args) throws IOException {
        //构造方法中可以是绝对路径,也可以是相对路径,我们常用正斜杠/来写,因为用反斜杠\的话,会被当成转义字符,需要写成E:\\QQMusicCache\\111\\text.txt
        File file = new File("e:/QQMusicCacha/111/text.txt");//绝对路径
        System.out.println(file.getParent());//获取文件的父目录
        System.out.println(file.getName());//获取文件名
        System.out.println(file.getPath());//获取文件的路径(创建File对象时指定的路径)
        System.out.println(file.getAbsolutePath());//获取文件的绝对路径
        System.out.println(file.getCanonicalPath());//获取文件简化过的绝对路径

        System.out.println("-----------------------");

        File file2 = new File("./text.txt");//相对路径
        System.out.println(file2.getParent());
        System.out.println(file2.getName());
        System.out.println(file2.getPath());
        System.out.println(file2.getAbsolutePath());
        System.out.println(file2.getCanonicalPath());
}

结果:

e:\QQMusicCacha\111
text.txt
e:\QQMusicCacha\111\text.txt
e:\QQMusicCacha\111\text.txt
E:\QQMusicCacha\111\text.txt
-----------------------
.
text.txt
.\text.txt
D:\Javacode\20220320\.\text.txt
D:\Javacode\20220320\text.txt

Process finished with exit code 0

我们看到File file2 = new File(“./text.txt”)并没有指定基准路径,我们怎么知道呢?

其实基准路径是由运行Java这个程序来确定的,不同的方式来运行java程序,基准路径就不一样,如果是通过IDEA的方式来运行Java程序,此时的基准路径就是当前Java项目所在的路径。

示例二

public static void main(String[] args) {
     File file = new File("e:/QQMusicCache/111/text.txt");
     System.out.println(file.exists());//文件是否存在
     System.out.println(file.isDirectory());//是否是目录
     System.out.println(file.isFile());//是否是普通文件
}

结果:

true
false
true

Process finished with exit code 0

示例三

普通文件的创建和删除

public static void main(String[] args) throws IOException {
    File file = new File("e:/QQMusicCache/111/text.txt");
    System.out.println(file.createNewFile());//创建一个空文件,成功返回true
    System.out.println(file.exists());
    System.out.println(file.delete());//删除一个文件,成功返回true
    System.out.println(file.exists());

结果:

true
true
true
false

Process finished with exit code 0

实例四

目录的创建

public static void main(String[] args) {
    File file = new File("./abc");
    //一次只能创建一级目录,如果已有这个目录,就会返回false
    System.out.println(file.mkdir());
    System.out.println(file.isDirectory());
    //想要一次创建多级目录,要用mkdirs()
    File file2 = new File("./abc/bb/cc");
    System.out.println(file2.mkdirs());
    System.out.println(file2.isDirectory());
}

结果:

true
true
true
true

Process finished with exit code 0

3.文件内容的操作

我们可以打开文件,读文件,写文件,关闭文件,针对文件内容的读写,Java中提供了一组类。

根据文件内容,分成两个系列:

1.字节流对象,针对二进制文件,以字节为单位进行读写。

  • 读InputStream,子类FileInputStream
  • 写OutputStream,子类FileOutputStream

2.字符流对象,针对文本文件,以字符为单位进行读写。

  • 读Reader,子类FileReader
  • 写Writer,子类FileWriter

因为InputStream等这些类是抽象类,不能通过new对象创建实例,实际使用的是这些类的子类。

什么是流对象

类似我们常说的水流,如果通过一个水龙头接100ml的水,我们可以1次接10ml,分10次接完,也可以1次接100ml,分1次接完

而我们通过这个流对象来读取100个字节的文件,我们可以1次读10个字节,分10次读完,也可以1次读100个字节,分1次读完

3.1 InputStream

InputSream方法

修饰符符及返回值类 型方法说明
intread()一次读取一个字节的数据,返回值是读到的这个字节,返回 -1 代表完全读完了
intread(byte[] b)一次读取若干个字节的数据,再放到指定的数组中,返回值是读到字节数;返回-1 代表读完了
intread(byte[] b, int off, int len)一次读取若干个字节的数据,再放到指定的数组中,返回值是读到字节数,从数组的off位置开始放置,最多能放len个元素;返回-1 代表读完了
voidclose()关闭字节流

FileInputSt构造方法

构造方法说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)可以是绝对路径,也可以是相对路径构造文件输入流

假设我的text.txt文件有abcd的内容.

read方法无参版本代码演示:

public static void main(String[] args) throws FileNotFoundException {
    //创建对象,也就打开了这个文件
    try (InputStream inputStream = new FileInputStream("e:/QQMusicCache/aaa/text.txt")) {
        while (true) {
            //无参数版本,一个一个字节地读,返回值是读到的字节
            int len = inputStream.read();
            if (len == -1) {
                //文件读完了
                return;
            }
            System.out.println(len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

97
98
99
100

Process finished with exit code 0

我们读写文件后,都需要调用close方法回收资源,Java中提供了try with resource的语法,它会在try语句块执行完之后自动调用close,但是需要符合一定的条件才能放到try()里面,要求都实现Closeable这个接口,才能放到()里面,而流对象都实现了这个接口

read方法一个参数版本演示:

public static void main(String[] args) throws FileNotFoundException {
    try (InputStream inputstream = new FileInputStream("e:/QQMusicCache/aaa/text.txt")) {
        while (true) {
            byte[] array = new byte[1024];
            //一次读取若干个字节,读到的数据放到数组中,返回值是读到的字节数
            int len = inputstream.read(array);
            System.out.println(len);
            if (len == -1) {
                //文件读完了
                return;
            }
            for (int i = 0; i < len; i++) {
                System.out.println(array[i]);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

4
97
98
99
100
-1

Process finished with exit code 0

read方法三个参数版本代码演示:

public static void main(String[] args) throws FileNotFoundException {
    try (InputStream inputStream = new FileInputStream("e:/QQMusicCache/aaa/text.txt")) {
        while (true) {
            byte[] array = new byte[1024];
            //三个参数版本,一次读取若干个字节的数据,再放到指定的数组中,返回值是读到字节数,从数组的off位置开始放置,最多能放len个元素
            int len = inputStream.read(array, 0, 2);
            System.out.println(len);
            if (len == -1) {
                return;
            }
            for (int i = 0; i < len; i++) {
                System.out.println(array[i]);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

2
97
98
2
99
100
-1

Process finished with exit code 0

3.2 OutputStream

方法

修饰符及返回值类型方法说明
voidwrite(int b)写入要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写入 os 中
voidwrite(byte[] b, int off, int len)将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
voidclose()关闭字节流
voidflush()重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为 了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的 一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写 入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的 数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置, 调用 flush(刷新)操作,将数据刷到设备中

write(byte[] b) 代码演示:

public static void main(String[] args) throws FileNotFoundException {
    try (OutputStream outputStream = new FileOutputStream("e:/QQMusicCache/aaa/text.txt")) {
        byte[] array = {'a', 'b', 'c', 'd', 'e'};
        outputStream.write(array);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

我们再打开这个文件:

在这里插入图片描述

其他方法也是类似,但是每次按照写方式打开文件,都会清空原来文件的内容,再从起始位置进行写入。

3.3Reader

read(char[] cubf)方法代码演示:

 public static void main(String[] args) throws FileNotFoundException {
     //以字符来读
     try (Reader reader = new FileReader("e:/QQMusicCache/aaa/text.txt")) {
         while (true) {
             char[] array = new char[1024];
             int len = reader.read(array);
             if (len == -1) {
                 return;
             }
             for (int i = 0; i < len; i++) {
                 System.out.println(array[i]);
             }
             //以字符串的形式输出
             String s = new String(array, 0, len);
             System.out.println(s);
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
 }

3.4 Writer

write(String str)方法代码演示:

public static void main(String[] args) {
    try (Writer writer = new FileWriter("e:/QQMusicCache/aaa/text.txt")) {
        //以字符来写
        writer.write("abcdefg");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

打开text.txt文件

在这里插入图片描述

字符流进行读写其实和字节流进行读写的方法都是类似的。

3.5文件操作的案例

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

public static void main(String[] args) {
    //1.用户输入要扫描的目录和要删除的文件名
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入要扫描的路径:");
    String rootDirPath = scanner.next();
    System.out.println("请输入要删除的文件名:");
    String deleteName = scanner.next();
    File rootDir = new File(rootDirPath);
    if (!rootDir.isDirectory()) {
        //判断用户输入的路径是否是目录
        return;
    }
    //2.把这个目录下的所有普通文件和子目录都遍历一遍
    //如果是普通文件,就查看普通文件的名是否包含要删除的文件名
    //如果是目录,因为文件和目录是通过树形结构管理的,所以要递归遍历
    scanDir(rootDir, deleteName);
}

private static void scanDir(File rootDir, String deleteName) {
    //列出这个目录下有哪些内容
    File[] files = rootDir.listFiles();
    if (files == null) {
        //这个目录为空
        return;
    }
    for (File f : files) {
        if (f.isFile()) {
            //普通文件的情况
            if (f.getName().contains(deleteName)) {
                if (f.isFile()) {
                    //询问用户是否要删除这个文件
                    deleteFile(f);
                }
            }
        } else if (f.isDirectory()) {
            //目录的情况
            scanDir(f, deleteName);
        }
    }
}

private static void deleteFile(File f) {
    Scanner scanner = new Scanner(System.in);
    try {
        System.out.println("确认要删除 " + f.getCanonicalPath() + " Y/N");
        String choice = scanner.next();
        if (choice.equals("Y") || choice.equals("y")) {
            System.out.println("删除成功");
            f.delete();
        } else {
            System.out.println("取消删除");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

案例二:进行普通文件的复制,让用户指定两个文件路径,打开源路径的文件,读取里面的内容,再写入目标文件中。

public static void main(String[] args) throws FileNotFoundException {
    //1.用户输入源路径和目标路径
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入要复制源路径:");
    String src = scanner.next();
    System.out.println("请输入要复制目标路径:");
    String dest = scanner.next();
    File srcFile = new File(src);
    if (!srcFile.isFile()) {
        //判断文件是否存在
        System.out.println("源路径输入错误");
        return;
    }
    //此处不需要检查目标文件是否存在. 因为OutputStream写文件的时候能够自动创建不存在的文件.
    //2.读取源文件,把里面的内容复制到目标文件中
    try (InputStream inputStream = new FileInputStream(src)) {
        //把inputStream读到的内容写入OutputStream中
        try (OutputStream outputStream = new FileOutputStream(dest)) {
            byte[] array = new byte[1023];
            while (true) {
                int len = inputStream.read(array);
                if (len == -1) {
                    //文件读到了末尾
                    return;
                }
                //不需要把整个数组都写入,只把有效数据写入
                outputStream.write(array, 0, len);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

请输入要复制源路径:
e:/QQMusicCache/aaa/text.txt
请输入要复制目标路径:
e:/QQMusicCache/text.txt

Process finished with exit code 0

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

public static void main(String[] args) {
    //1.用户输入路径和要指定的字符
    Scanner scanner = new Scanner(System.in);
    System.out.println("请输入要扫描的路径:");
    String rootDirPath = scanner.next();
    System.out.println("请输入要找的文件名:");
    String findName = scanner.next();
    //判断路径是否是目录
    File rootDir = new File(rootDirPath);
    if (!rootDir.isDirectory()) {
        System.out.println("输入的路径有误");
        return;
    }
    //2.把这个目录下的所有普通文件和子目录都遍历一遍
    //如果是普通文件,就查看普通文件中是否包含有要指定字符的名字
    //如果是目录,就递归遍历
    scanDir(rootDir, findName);
}
private static void scanDir(File rootDir, String findName) {
    //1.先列出rootDir里面都有哪些内容
    File[] files = rootDir.listFiles();
    //判断目录是否为空
    if (files == null) {
        return;
    }
    for (File f : files) {
        //普通文件的情况
        if (f.isFile()) {
            if (f.getName().contains(findName)) {
                try {
                    System.out.println(f.getCanonicalPath());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else if (f.isDirectory()) {
            //目录的情况
            scanDir(f, findName);
        }
    }
}

结果:

请输入要扫描的路径:
e:/QQMusicCache
请输入要找的文件名:
text.txt
E:\QQMusicCache\aaa\text.txt
E:\QQMusicCache\text.txt

Process finished with exit code 0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

那年盛夏繁如花

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

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

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

打赏作者

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

抵扣说明:

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

余额充值