【Java】文件操作 详解

文件的分类:

  1. 文本文件:里面存储的是字符,文本文件本质上存储的也是字节,但文本文件中相邻字节在一起正好构成一个字符。
  2. 二进制文件:里面存储的是字节,字节和字节之间没有什么关系。

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

一. File 类

1. 属性

在这里插入图片描述

2. 构造方法

在这里插入图片描述

3. 方法

在这里插入图片描述

getCanonicalPath() : 简化过后的路径。
比如:

./home/../test 等价于 ./test

代码示例:

  • 示例1:观察 get 系列的特点和差异
    public static void main(String[] args) throws IOException {
        File file = new File("..\\hello-world.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());
    }

运行结果:

在这里插入图片描述

  • 示例2:普通文件的创建
    public static void main(String[] args) throws IOException {
        File file = new File("hello-world.txt"); // 要求该文件不存在,才能看到相同的现象
        System.out.println(file.exists()); // false
        System.out.println(file.isDirectory()); // false
        System.out.println(file.isFile()); // false
        // 创建文件
        System.out.println(file.createNewFile()); // true 
        System.out.println(file.exists()); // true 
        System.out.println(file.isDirectory()); // false
        System.out.println(file.isFile()); // true 
        System.out.println(file.createNewFile()); // false
    }
  • 示例3:普通文件的删除
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
        System.out.println(file.exists()); // false
        System.out.println(file.createNewFile()); // true
        System.out.println(file.exists()); // true
        System.out.println(file.delete()); // true
        System.out.println(file.exists()); // false
    }
  • 示例4:观察 deleteOnExit 的现象
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求该文件不存在,才能看到相同的现象
        System.out.println(file.exists()); // false
        System.out.println(file.createNewFile()); // true
        System.out.println(file.exists()); // true
        file.deleteOnExit();
        System.out.println(file.exists()); // true
    }

文件等到程序退出才被删除,所以调用过 deleteOnExit 再进行判断文件是否存在还是 true,但是我们去查看目录就会发现文件被删了。

  • 示例5:目录的创建
    public static void main(String[] args) throws IOException {
        File dir = new File("some-dir"); // 要求该目录不存在,才能看到相同的现象
        System.out.println(dir.isDirectory()); // false
        System.out.println(dir.isFile()); // false
        System.out.println(dir.mkdir()); // true
        System.out.println(dir.isDirectory()); // true
        System.out.println(dir.isFile()); // false
    }

注意:文件/目录创建出来之前它什么都不是,只是一个 File 对象。

  • 示例6:目录创建2
    public static void main(String[] args) throws IOException {
        File dir = new File("some-parent\\some-dir"); // some-parent 和 some-dir 都不存在
        System.out.println(dir.isDirectory()); // false
        System.out.println(dir.isFile()); // false
        System.out.println(dir.mkdir()); // false
        System.out.println(dir.isDirectory()); // false
        System.out.println(dir.isFile()); // false
    }

mkdir() 的时候,如果中间目录不存在,则无法创建成功; mkdirs() 可以解决这个问题。

    public static void main(String[] args) throws IOException {
        File dir = new File("some-parent\\some-dir"); // some-parent 和 some-dir 都不存在
        System.out.println(dir.isDirectory()); // false
        System.out.println(dir.isFile()); // false
        System.out.println(dir.mkdirs()); // true
        System.out.println(dir.isDirectory()); // true
        System.out.println(dir.isFile()); // false
    }

mkdirs() 同时创建多级目录。

  • 示例7:文件重命名
    public static void main(String[] args) throws IOException {
        File file = new File("some-file.txt"); // 要求 some-file.txt 得存在,可以是普通文件,可以是目录
        File dest = new File("dest.txt");   // 要求 dest.txt 不存在
        System.out.println(file.exists()); // true
        System.out.println(dest.exists()); // false
        System.out.println(file.renameTo(dest)); // true
        System.out.println(file.exists()); // false
        System.out.println(dest.exists()); // true
    }

二. 文件读写

针对文件内容的读写,Java 标准库提供了一组类,按照文件内容分为两个系列:

在这里插入图片描述
注意:这些抽象类的实现类有很多,这里只列举了几个常见的。
虽然类有很多,但是使用都是差不多的,这里以 InputStream 和 OutputStream 为例。

1. InputStream

方法

在这里插入图片描述

注意: 使用完资源要记住关闭 close()

  1. 写到 finally 里面, 但是这么写太麻烦了,于是 Java 提供了 try with resources.
  2. try with resources. 后面的代码中也有体现.
    代码:
        try (InputStream is = new FileInputStream("input.txt")) {
            // do something
        }

代码中没有显式调用 close(), 但是 try 会帮我们自动调用,执行完 try 语句块后,自动帮我们调用 close().
注意:想要使用 try with resources 必须实现 Closeable 接口,所有的流对象都实现了这个接口。

2. FileInputStream

InputStream 是一个抽象类,FileInputStream 是 InputStream 的一个实现类。

构造方法

在这里插入图片描述

代码示例:

  • 示例1:将文件完全读完的两种方式。

一次读取一个字节

    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            while (true) {
                int b = is.read();
                if (b == -1) {
                    // 代表文件已经全部读完
                    break;
                }

                System.out.printf("%c", b);
            }
        }
    }

一次读取多个字节

    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;

            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
                }

                for (int i = 0; i < len; i++) {
                    System.out.printf("%c", buf[i]);
                }
            }
        }
    }

相比较而言,后一种的 IO 次数更少,性能更好。

  • 示例2:文件内容中填充中文并读完。

注意,这里面写中文的时候使用的是 UTF-8 编码。

    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            byte[] buf = new byte[1024];
            int len;
            while (true) {
                len = is.read(buf);
                if (len == -1) {
                    // 代表文件已经全部读完
                    break;
                }
                // 每次使用 3 字节进行 utf-8 解码,得到中文字符
                // 利用 String 中的构造方法完成
                // 这个方法了解下即可,不是通用的解决办法
                for (int i = 0; i < len; i += 3) {
                    String s = new String(buf, i, 3, "UTF-8");
                    System.out.printf("%s", s);
                }
            }
        }
    }

注意:这里利用了这几个中文的 UTF-8 编码后长度刚好是 3 个字节和长度不超过 1024 字节的现状,所以这种方式并不是通用的。

3. 使用 Scanner 进行字符读取

字符类型使用 InputStream 读取非常麻烦且困难,所以,我们使用比较熟悉的Scanner 类完成该工作 。

在这里插入图片描述

    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello.txt")) {
            try (Scanner scanner = new Scanner(is, "UTF-8")) {
                while (scanner.hasNext()) {
                    String s = scanner.next();
                    System.out.print(s);
                }
            }
        }
    }

4. OutputStream

在这里插入图片描述
flush():

  • 我们知道 I/O 的速度是很慢的。
  • 因为一次写一点, 分多次写当然没有一次攒一堆,统一 一次写完更高效,读操作也类似。
  • 所以, OutputStream 为减少设备操作次数,在写数据时都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域称为缓冲区。
  • 这会造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。

注意:close 操作也会触发缓冲区刷新。

5. FileOutputStream

OutputStream 是一个抽象类,FileOutputStream是 OutputStream 的一个实现类。

在这里插入图片描述
注意:

  1. 按照写的方式打开文件每次都会清空原本文件的内容,从起始位置开始写。
  2. 想要使用追加的方式,必须使用带有 boolean append 的构造方法并传入 true。

利用 OutputStream 进行字符写入:

  • 代码示例:

单个字节的写

    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            os.write('H');
            os.write('e');
            os.write('l');
            os.write('l');
            os.write('o');
            // 不要忘记 flush
            os.flush();
        }
    }

一下写入一个字节数组中的数据

    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            byte[] b = new byte[] {
                    (byte)'G', (byte)'o', (byte)'o', (byte)'d'
            };
            os.write(b);
            // 不要忘记 flush
            os.flush();
        }
    }

将字节数组的某个区间写入

    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            byte[] b = new byte[] {
                    (byte)'G', (byte)'o', (byte)'o', (byte)'d', (byte)'B',
                    (byte)'a', (byte)'d'
            };
            os.write(b, 0, 4);

            // 不要忘记 flush
            os.flush();
        }
    }

将一个字符串中的内容写入

    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            String s = "Nothing";
            byte[] b = s.getBytes();
            os.write(b);

            // 不要忘记 flush
            os.flush();
        }
    }

写入中文

    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            String s = "你好中国";
            byte[] b = s.getBytes("utf-8");
            os.write(b);
            // 不要忘记 flush
            os.flush();
        }
    }

6. 使用 PrintWriter 进行字符写入

上述已经完成输出工作,但总有所不方便,我们将 OutputStream 处理下,使用 PrintWriter 类来完成输出,因为 PrintWriter 类中提供了我们熟悉的 print/println/printf 方法

    public static void main(String[] args) throws IOException {
        OutputStream os = new FileOutputStream("output.txt");
        OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8"); // 告诉它,我们的字符集编码是 utf-8 的
        PrintWriter writer = new PrintWriter(osWriter);
        // 接下来我们就可以方便的使用 writer 提供的各种方法了
        writer.print("Hello");
        writer.println("你好");
        writer.printf("%d: %s\n", 1, "没什么");
        // 不要忘记 flush
        writer.flush();
    }

好啦! 以上就是对 文件操作 的讲解,希望能帮到你 !
评论区欢迎指正 !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值