文件操作详解

本文详细介绍了Java中文件操作的相关知识,包括路径的绝对路径和相对路径概念,File类的构造方法和常用方法,以及字节流和字符流在文件读写中的应用。此外,还讨论了Scanner类在读取文件内容时的角色,并提供了两个案例,分别是扫描目录删除特定文件和高效拷贝文件的实现。
摘要由CSDN通过智能技术生成

目录

前言:

路径理解

绝对路径

相对路径

File类

构造方法

普通方法

代码演示

文件内容读写

字节流

代码演示

代码演示

字符流

代码演示

 再谈Scanner

代码演示

案例一

代码实现

案例二

代码实现

小结


前言:

    我们平时写的局部变量,数组之类的数据都是存储在内存中。内存中的数据只要电脑断电,数据就会丢失。而对于硬盘里的数据,只要硬盘不坏,数据就会一直保存。文件操作就是直接操作硬盘里的数据。

路径理解

绝对路径

    在Windows中,描述某个文件从盘符开始写起,到指定文件。

    例如:E:\java代码\SwordOffer

    在Linux中,从/(根目录开始),到指定文件。pwd查看的就是绝对路径。

    例如:/home/wh/Code

相对路径

    相对路径是指相对于某个路径(一般称为工作目录),另一个文件的路径。idea的工作目录就是项目所在的目录。相对路径就可以用工作目录做为参考来描述另一个文件路径。.代表当前目录,..代表上级目录。

    在windows中:./test.txt。意思为在当前工作目录下的test.txt文件。

    在Linux中:./game。意思为当前工作目录下的game目录。

File类

构造方法

    注意:创建的File实例就是明确的指向了一个文件,或者目录。通过这个实例就可以对于这个文件或者目录进行一些操作。

普通方法

    这里的普通方法使用起来比较简单,我们看见方法名,就可以知道这个方法是做什么的。我直接用代码演示(带有详细注释)。

    这里我介绍一下list()方法和listFiles()方法。第一个返回file对象代表的目录下所有文件名,如果代表文件则返回null。第二个返回file对象代表的目录下所有文件,以file对象表示。

代码演示

public class IODemo1 {
    public static void main(String[] args) throws IOException {
        //相对路径,file对象就指向这个文件
        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());
        //是否为目录
        System.out.println(file.isDirectory());
        //是否为文件
        System.out.println(file.isFile());
        //文件长度
        System.out.println(file.length());
        //是否支持写功能
        System.out.println(file.canWrite());
        //是否支持读功能
        System.out.println(file.canRead());
        //是否可以执行
        System.out.println(file.canExecute());
        //是否存在这个文件
        System.out.println(file.exists());
        //是否为隐藏文件
        System.out.println(file.isHidden());
        //返回file对象代表的目录下所有文件名,如果代表文件则返回null
        String[] tmp = file.list();
        System.out.println(Arrays.toString(tmp));
        //返回file对象代表的目录下所有文件,以file对象表示
        File[] tmp2 = file.listFiles();
        System.out.println(tmp2.length);

        File file0 = new File("./test.txt");
        File tmp0 = new File("./test3.txt");
        //修改文件名
        //System.out.println(file.renameTo(tmp));
        //创建目录(file对象所指向的目录)
        System.out.println(file0.mkdir());
        File file1 = new File("./test/test2/test3");
        //创建目录(file对象所指向的一连串目录)
        System.out.println(file1.mkdirs());
        //删除file对象所指向的目录
        System.out.println(tmp0.delete());
        //程序退出时,删掉file对象所指向的文件(临时文件)
        tmp0.deleteOnExit();

        File file2 = new File("./test2.txt");
        //创建一个file所指向的文件
        System.out.println(file2.createNewFile());
        System.out.println(file2.delete());
    }
}

文件内容读写

    这里涉及流对象。对于“流”的理解,我们可以认为数据的写入,读出就像水流一样。

    例如我们想盛满一杯水,可以一次就盛满,也可以分多次。读写数据,我们一次可以写一部分数据,也可以一次写完。读数据也是如此。

字节流

InputStream方法介绍 

    1)尽可能一次读取这个数组最大长度的字节数,如果数据量不足以这个数组的最大长度,即有多少就读多少,并且返回读取到的长度。如果读到文件结束标志,则返回-1

    2)一次只读一个字节,返回读到的数据。由于读到文件结尾时,返回-1,所以返回值用int表示才够用。

    3)关闭文件。创建流对象时,就会打开文件。在操作系统中一个进程的描述用pcb来表示。其中就包含了文件描述符表(其实就是一个数组),这个数组的大小是可以修改的,但不管怎么样它是有限的。每当我们打开一个文件,就会在文件描述符表中申请一块资源。如果我们只打开文件,不去关闭文件(释放文件描述符表中的资源),总会有表满的时候。因此在不需要这个文件时,就可以关闭文件,释放资源。

    4)读取文件到这个字节数组,从off位置到len的长度。如果读到文件结束标志,就返回-1。

注意:

    1)InputStream是抽象类,要使用还得实现具体的类。关于InputStream的实现类有很多,这里只关注读写文件,所以使用FileInputStream。

    2)FileInputStream的构造方法中可以指定文件路径或者使用File对象。

代码演示

使用数组来读取

public class IODEmo8 {
    public static void main(String[] args) throws IOException {
        File file = new File("E:/tmp/dog.png");
        InputStream inputStream = new FileInputStream(file);
        while (true) {
            //缓冲区数组。可以减少IO次数,相比于一次读一个字节
            byte[] buffer = new byte[1024];
            int len = inputStream.read(buffer);
            System.out.println(len);
            if(len == -1) {
                break;
            }
        }
        inputStream.close();
    }
}

    注意:可以看见读取数据量够1024时,就读取1024个字节,不足时读取剩下的数据。读到文件结束标志时返回-1。

一次读取一个字节

 public static void main(String[] args) throws IOException {
        File file = new File("./test.txt");
        //创建操作这个文件的流对象,并且打开这个文件
        //可以用file对象,或者路径的方式构造流对象
        InputStream inputStream = new FileInputStream(file);
        while (true) {
            //读文件流
            //不带参数以字节为单位,读取后返回
            //带数组参数,读取数据存入数组中
            int tmp = inputStream.read();
            //读到文件结尾返回-1
            if(tmp == -1) {
                break;
            }
            System.out.printf("%x\n", tmp);
        }
        inputStream.close();
    }

    注意:一次读取一个字节,这里以16进制的方式表示。

OutputStream方法介绍

    1)将字节数组写入指定的文件中。

    2)将传入的数据b写入指定文件中。

    3)往文件中写数据,首先是写到了文件缓冲区中,当这个文件缓冲区到达一定的数据量,就会刷新文件缓冲区,将内容写入文件中(同时清空文件缓冲区内容)。有时候我们为了确保数据写入到了文件中,就可以手动刷新文件缓冲区。close()方法也会触发刷新文件缓冲区的操作。

    4)将字节数组,从off位置到len长度写入指定文件。

    5)关闭文件,这里原理和字节流的关闭文件一致。

注意:

    1)同样的OutputStream也是一个抽象类,使用需要具体的实现类。和上面一样这里使用FileOutputStream。

    2)OutputStream在写文件时,如果这个文件不存在就会自动创建这个文件。

    3)OutputStream在写文件时,会清空文件中原来的数据。

    4)上文中解释了,close()方法的原理,但是像上文的写法,close()方法不一定会执行到。我们为了确保close()方法在不使用文件时一定可以执行,使用带有资源的try () {}的语法。这里流对象使用完就会自动调用close()方法(只有实现了Closeable类的方法,才可以这样写),下面代码就会使用。

代码演示

写入字符

public class IODEmo8 {
    public static void main(String[] args) throws IOException {
        //为了保证close被执行使用带有资源的try:try() {}
        //只有实现了Closeable类的方法,才可以这样写
        //这样会自动执行close()方法
        File file = new File("E:/tmp/test.txt");
        try(OutputStream outputStream = new FileOutputStream(file)) {
            outputStream.write(97);
            //手动刷新缓冲区
            outputStream.flush();
        }
        //写操作,实际是将内容先写入缓冲区,等待缓冲区到达了一定程度,就会刷新到硬盘中
        //flush()方法手动刷新缓冲区
    }
}

    注意:可以看见字符a已经写入了文件中。

写入字符数组

public class IODEmo8 {
    public static void main(String[] args) throws IOException {
        File file = new File("E:/tmp/test.txt");
        try(OutputStream outputStream = new FileOutputStream(file)) {

            outputStream.write(new byte[]{'a', 'b', 'c'});
            //手动刷新缓冲区
            outputStream.flush();
        }
    }
}

    注意:可以清楚看见abc已经写入了文件,并且清空了文件原有的内容。

字符流

    字符流和字节流操作几乎一致,这里不做过多介绍,直接代码演示,大家就可以明白。

    读文件:Reader类。

    写文件:Writer类或者PrintWriter类。

注意:

    1)Writer和Reader都是抽象方法,使用需要其具体的实现类。Writer这里使用FileWrite类,Reader这里使用FileReader类。

    2)PrintWriter类可以将字节流转换为字符流,直接将OutputStream字节流对象写入PrintWriter类的构造方法中即可。

代码演示

public class IODEmo8 {
    public static void main(String[] args) throws IOException {
       File file = new File("E:/tmp/test.txt");
       try(PrintWriter printWriter = new PrintWriter(file);
           Writer writer = new FileWriter(file);
           Reader reader = new FileReader(file)) {
           //写入字符串
           printWriter.print("aa");
           //writer.write("aa");
           //刷新缓冲区
           printWriter.flush();
           //读文件
           while (true) {
               int tmp = reader.read();
               System.out.println(tmp);
               if(tmp == -1) {
                   break;
               }
           }
       }catch (IOException e) {
           e.printStackTrace();
       }
    }
}

再谈Scanner

    Scanner之前在使用时,参数写的System.in。这其实就是打开了标准输入流(键盘),close()方法也是关闭的这个流对象。

    那我们也可以写我们创建的流对象,它就会从这个对象所打开的文件中读取数据。

代码演示

public class IODEmo8 {
    public static void main(String[] args) throws IOException {
        File file = new File("E:/tmp/test.txt");
        try(InputStream inputStream = new FileInputStream(file)) {
            //将指定流对象写入Scanner参数中
            Scanner scanner = new Scanner(inputStream);
            //读取数据
            while (scanner.hasNext()) {
                String tmp = scanner.next();
                System.out.println(tmp);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意:

    这里next()方法的读取规则:一次读取遇到空白符即结束读取,读取的内容不包含空白符。

案例一

    扫描指定目录,并找到名称中包含指定字符的所有指定文件(不包含目录)

核心思想:

    进入指定目录后,显示目录中所有文件的File对象。遍历File数组,是目录就递归进去,否则判断文件名字是否包含指定字符,是就删除文件即可。

代码实现

public class IODemo5 {
    private static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.println("请输入你要扫描目录的路径:");
        String path = scanner.next();
        File file = new File(path);
        if(!file.isDirectory()) {
            System.out.println("输入错误");
            return;
        }
        System.out.println("请输入你要删除的文件的字符");
        String str = scanner.next();
        fun(file, str);

    }
    private static void fun(File root, String str) {
        //获得该目录下所有文件包括目录对象
        File[] files = root.listFiles();
        //递归结束
        if(files == null) {
            return;
        }
        for(File file : files) {
            //是目录就递归
            if(file.isDirectory()) {
                fun(file, str);
            }else {
                //删除文件
                String name = file.getName();
                if(name.contains(str)) {
                    file.delete();
                }
            }
        }
    }
}

案例二

    拷贝文件到指定路径。

核心思想:

    提高拷贝效率(减少IO次数,需要消耗系统资源)。设置缓冲数组,读数据到缓冲数组,将缓冲数组写入指定路径。直到文件读取结束,拷贝也就结束。

代码实现

public class IODemo6 {
    private static Scanner scanner = new Scanner(System.in);
    public static void main(String[] args) {
        System.out.println("输入你要拷贝文件路径");
        String srcPath = scanner.next();
        File srcFile = new File(srcPath);
        //如果不是文件
        if(!srcFile.isFile()) {
            System.out.println("输入错误");
            return;
        }
        System.out.println("输入拷贝目的地路径");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        //如果拷贝目的文件已存在
        if(destFile.isFile()) {
            System.out.println("输入错误");
            return;
        }
        copyFiles(srcFile, destFile);
    }

    private static void copyFiles(File srcFile, File destFile) {
        //边读边拷贝
        //读源头文件,写目的地文件
        try(InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile)) {
            //边读边写
            while (true) {
                byte[] buffer = new byte[1024];
                int len = inputStream.read(buffer);
                if(len == -1) {
                    break;
                }
                outputStream.write(buffer);
            }

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

小结:

    初心不变,奋斗不止!与大家共勉。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小太空人w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值