Java中的文件操作(基础知识+三个小程序练习)

2年前 (2022) 程序员胖胖胖虎阿
242 0 0

文件操作

关于文件,自然不用多说。狭义上的文件。针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。

文件除了有数据内容之外,还有一部分信息,例如文件名、文件类型、文件大小等并不作为文件的数据而存在,我们把这部分信息可以视为文件的元信息。

文件的路径分为绝对路径相对路径

以盘符开头的路径为绝对路径,如:D:\program\qq\Bin\QQ.exe

以 . 或 … 开头的路径为相对路径,如: .\test.txt

Java中的文件操作

Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。

注意,有 File 对象,并不代表真实存在该文件

1 File概述

我们先来看看 File 类中的常见属性、构造方法和方法

属性

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

构造方法

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

方法

修饰符及返回 值类型 方法签名 说明
String getParent() 返回 File 对象的父目录文件路径
String getName() 返回 FIle 对象的纯文件名称
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() 判断用户是否对文件有可写权限

1.1 代码示例

示例一:get系列

public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");
        System.out.println(file.getParent());
        System.out.println(file.getName());
        System.out.println(file.getPath());
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalPath());
    }

Java中的文件操作(基础知识+三个小程序练习)

示例二:判断文件是否存在,判断文件、目录,创建文件

public static void main(String[] args) throws IOException {
        // 前面没写 './' ,也相当于是'./'  ('./' 可以省略)
        File file = new File("helloworld.txt");
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());
        System.out.println("=============");
        //创建文件
        file.createNewFile();
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println(file.isFile());

示例三:删除文件

public static void main(String[] args) throws InterruptedException {
        //文件删除
        File file = new File("helloworld.txt");
        //file.delete();  立即删除

        Thread.sleep(5000);
        file.deleteOnExit();//程序退出时才删除(用来创建一些临时文件)
        System.out.println(file.exists());

    }

示例四:创建目录

//创建目录
    public static void main(String[] args) {
        File file = new File("test");
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        System.out.println("================");
        file.mkdir();
        System.out.println(file.exists());
        System.out.println(file.isDirectory());
        //创建多级目录
        //File file = new File("test/aa/1");
        //file.mkdirs();
    }

示例五:文件重命名

//文件重命名
    public static void main(String[] args) {
        File file1 = new File("./test.txt");
        File file2 = new File("./test2.txt");
        file1.renameTo(file2);// 用 file2 的名字给 file1 命名
    }

2 文件的读写操作——数据流

Java中的文件操作(基础知识+三个小程序练习)

2.1 读文件

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() 关闭字节流

InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基
本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使
用 FileInputStream

read的不同使用方式

Java中的文件操作(基础知识+三个小程序练习)

利用FileInputStream进行读取文件

示例一

我们为了能让close一定被执行,这样做

    public static void main(String[] args)  {
        //使用一下 InputStream
        InputStream inputStream = null;
        try {
            //1.打开文件
            inputStream = new FileInputStream("./test2.txt");

            //2.读取文件
//        while(true){
//            int b = inputStream.read();
//            if(b == -1){
//                //文件读完了
//                break;
//            }
//            System.out.println(b);
//        }

            byte[] b = new byte[1024];
            int len = inputStream.read(b);
//        System.out.println(len);
//        for (int i = 0; i < len; i++) {
//            System.out.println(b[i]);
//        }
            String s = new String(b,0,len,"utf8");
            System.out.println(s);
        } catch (IOException e ){
            e.printStackTrace();
        } finally {
            //3.关闭文件
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Java中的文件操作(基础知识+三个小程序练习)

但是在finally里还有个try-catch 会让代码很冗杂

我们这样做

public static void main(String[] args)  {
        try(InputStream inputStream = new FileInputStream("test2.txt")){
            //读文件
            byte[] b = new byte[1024];
            int len = inputStream.read(b);
            String s = new String(b,0,len,"utf8");
            System.out.println(s);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

Java中的文件操作(基础知识+三个小程序练习)

代码二更为推荐

示例二:

当文件中的内容为 hello

Java中的文件操作(基础知识+三个小程序练习)

Java中的文件操作(基础知识+三个小程序练习)

使用Reader字符流读取文件

当文件中的内容变成了汉字:你好

Java中的文件操作(基础知识+三个小程序练习)

代码:

//使用字符流读一下文件
    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("test2.txt");
        char[] buffer = new char[1024];
        int len = reader.read(buffer);
        for(int i = 0; i< len; i++){
            System.out.println(buffer[i]);
        }
        reader.close();
    }

我们还有更简单的方法

利用 Scanner 进行字符读取

代码

// 对于文本文件  我们还有更简单的写法
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("test2.txt");

        Scanner scanner = new Scanner(inputStream);
        String s = scanner.next();
        System.out.println(s);

        inputStream.close();
    }

2.2 写文件

字节流:OutputStream/FileOutputStream

字符流:Writer/FileWriter

和 Scanner相对的,还可以使用 PrintWriter 来简化针对字符流的写入操作

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(刷新)操作,将数据刷到设备中

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,
所以使用 FileOutputStream

利用 OutputStreamWriter 进行字符写入

一个字符一个字符地写

public static void main(String[] args) {
        try(OutputStream outputStream = new FileOutputStream("test2.txt")){
            outputStream.write('h');
            outputStream.write('e');
            outputStream.write('l');
            outputStream.write('l');
            outputStream.write('o');
        }catch (IOException e){
            e.printStackTrace();
        }
    }

写入一个字符串

 public static void main(String[] args) {
        try(OutputStream outputStream = new FileOutputStream("test2.txt")){
            String s = "hello java";
            outputStream.write(s.getBytes());

        }catch (IOException e){
            e.printStackTrace();
        }
    }

我们发现,每次写的时候都会把原本文件中的内容清空

Java中的文件操作(基础知识+三个小程序练习)

是因为这行代码

使用Writer进行字符流写入

示例

public static void main(String[] args) {
        try(Writer writer = new FileWriter("test2.txt")){
            writer.write("hello world");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
利用 PrintWriter 进行字符写入
public static void main(String[] args) {
        try (OutputStream outputStream = new FileOutputStream("test2.txt")){
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.print("hello");
            printWriter.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

我们发现,与上面的方法不同的是 多了个 printWriter.flush();

解释:PrintWriter是自带了缓冲区的,所谓的缓冲区其实就是一块内存空间

那么缓冲区里的内容什么时候会被刷新到硬盘中呢?

  1. 缓冲区满了时
  2. 显式调用 flush (“冲水操作”)

所以在使用PrintWriter 时,别忘了要 flush

3 小程序练习

我们学会了文件的基本操作 + 文件内容读写操作,接下来,我们实现一些小工具程序,来锻炼我们的能力

3.1 文件查找并删除

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

如何遍历目录?

"递归"的把这里所有的文件(子目录中的文件)都能够访问到

public class Demo13 {
    //实现一个递归遍历文件,并询问删除
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要扫描的路径");
        String rootPath = scanner.next();
        File root = new File(rootPath);
        if(!root.exists()){
            System.out.println("路径不存在");
            return;
        }
        System.out.println("请输入要删除的文件的文件名或者文件名的一部分");
        String toDelete = scanner.next();

        //准备进行递归  通过递归的方式找到所有的文件
        //找到所有的文件之后 再尝试进行删除
        scanDir(root,toDelete);

    }

    /**
     * 扫描目录
     * @param rootDir
     * @param toDelete
     */
    public static void scanDir(File rootDir,String toDelete){
        //加上个日志  看一下这里当前递归的过程
        try{
            System.out.println(rootDir.getCanonicalPath());
        }catch (IOException e){
            e.printStackTrace();
        }
        File[] files = rootDir.listFiles();
        if(files == null){
            //空目录 直接返回
            return;
        }
        for(File f : files){
            if(f.isDirectory()){
                //是目录 就继续递归
                scanDir(f,toDelete);
            }else {
                //普通文件
                tryDelete(f,toDelete);
            }
        }
    }
    public static void tryDelete(File f , String toDelete){
        //查看当前文件名是否包含了 toDelete 如果包含就删除,否则什么都不做
        if(f.getName().contains(toDelete)){
            try{
                System.out.println("是否要删除文件(Y/N):"+ f.getCanonicalPath());
                Scanner scanner = new Scanner(System.in);
                String choice = scanner.next();
                if(choice.equals("Y")){
                    f.delete();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

上述过程其实就是一个 “深度优先遍历/搜索”

3.2 复制一个普通文件

普通文件,即:不是目录文件

把文件1复制成文件2

把文件1里的内容都按照字节读取出来,写入到文件2中

//实现复制文件的功能
    public static void main(String[] args) {
        //
        //准备工作
        //

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要复制的文件路径:");
        String srcPath = scanner.next();
        File srcFile = new File(srcPath);
        if(!srcFile.exists()){
            System.out.println("文件不存在");
            return;
        }
        if(!srcFile.isFile()){
            System.out.println("要复制的不是普通文件");
            return;
        }
        System.out.println("请输入要复制到的目标路径");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        if(destFile.exists()){
            System.out.println("要复制到的目标已存在");
            return;
        }
        //
        //进行拷贝工作
        //
        try(InputStream inputStream = new FileInputStream(srcFile)) {
            try(OutputStream outputStream = new FileOutputStream(destFile)){
                byte[] buf = new byte[1024];
                while (true){
                    int len = inputStream.read(buf);
                    if(len == -1){
                        //拷贝完成
                        break;
                    }
                    outputStream.write(buf,0,len);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
        System.out.println("复制完成!");
    }

可以这样想象,在读文件的时候,在文件对象内部,有一个"光标",通过这个"光标"表示当前文件读到哪个位置了。

每次读操作,都会让光标往后移动, 直到文件末尾,再继续读,就会读到一个特殊的字符, EOF

3.3 文件深层查找(名称/内容)

public class Demo15 {
    //遍历目录,看某个输入的词是否在文件名或者文件内容中存在
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要搜索的目录:");
        String rootPath = scanner.next();
        File rootFile = new File(rootPath);
        if(!rootFile.exists()){
            System.out.println("该目录不存在");
            return;
        }
        if(!rootFile.isDirectory()){
            System.out.println("该路径不是目录");
            return;
        }
        System.out.println("请输入要搜索的关键词:");
        String toFind = scanner.next();

        //递归遍历目录
        scanDir(rootFile,toFind);
    }

    private static void scanDir(File rootFile, String toFind) throws IOException {
        File[] files = rootFile.listFiles();
        if(files == null){
            return;
        }
        for (File f : files) {
            if(f.isDirectory()){
                scanDir(f,toFind);
            }else {
                tryFindInFile(f,toFind);
            }
        }
    }

    //判定toFind是否是文件名或者是文件内容的一部分
    private static void tryFindInFile(File f, String toFind) throws IOException {
        //是不是文件名的一部分
        if(f.getName().contains(toFind)){
            System.out.println("找到了文件名与关键词匹配的文件:"+f.getCanonicalPath());
            return;
        }
        //是不是文件内容的一部分
        try(InputStream inputStream = new FileInputStream(f)){
            //把文件内容整个都读出来
            StringBuilder stringBuilder = new StringBuilder();
            Scanner scanner = new Scanner(inputStream);
            while (scanner.hasNextLine()){
                stringBuilder.append(scanner.nextLine());
            }
            //读取完毕了
            if(stringBuilder.indexOf(toFind) >= 0){
                System.out.println("找到了文件内容与关键词匹配的文件:"+f.getCanonicalPath());
                return;
            }
        }
    }
}

注:我们现在的方案性能较差,所以尽量不要在太复杂的目录下或者大文件下实验

相关文章

暂无评论

暂无评论...