JavaEE初阶 - 文件操作和IO

1. 什么是文件?
2. 文件的分类
3. 目录结构
4. 绝对路径和相对路径
5. 文件系统的操作(File类)
6. 文件内容的操作
    InputStream和OutputStream
    Reader和Writer
7. 文件操作的案例
    删除指定文件
    复制文件内容


1. 什么是文件?

  在计算机中, 文件是以计算机的磁盘为载体存储在计算机上的数据集合, 这些文件不仅包括普通文件(如txt, rar, zip, mp4等文件), 还可以包含目录.

2. 文件的分类

按照数据在文件中存储方式的不同, 我们将文件分成两类:

  1. 文本文件:存储的数据单位为字符, 如 .txt 文件, .java文件等
  2. 二进制文件:存储的数据单位为字节, 字节之间没有任何联系, 如 .exe 文件, .class文件等

3. 目录结构

  在计算机中, 保存管理文件是通过操作系统中的"文件系统"模块来负责的, 文件系统中一般是通过"树形结构"来组织磁盘中的文件, 一个普通文件就是一个叶子结点, 一个目录文件, 目录中就可以包含子树, 这就是一个非叶子结点, 每棵树的非叶子结点都可以有多个子节点, 这些树就都是N叉搜索树.

JavaEE初阶 - 文件操作和IO_第1张图片
例如, 上面这张图中, 每个文件夹都是一个非叶子节点, 而文件夹里的普通文件(例如上面的.jpg文件)就是一个叶子结点.

4. 绝对路径和相对路径

操作系统中, 通过"路径"来描述一个文件/目录的具体位置, 路径共有两种描述风格:

绝对路径:以盘符开头(例如:d:\test.txt)
相对路径:以 . 或者 … 开头, 其中 . 表示当前路径, … 表示当前路径的父目录

相对路径必须有一个基准目录, 相对路径的出发点就是基准目录

即使是同一个文件, 基准目录不同, 相对目录也不同

文件系统的操作(File类)

文件系统相关操作就是通过文件资源管理器能够完成的功能, 例如:

  1. 列出目录有哪些文件
  2. 创建文件
  3. 创建目录
  4. 删除文件
  5. 重命名文件

  在Java中提供了一个File类, 通过这个类完成上述操作, File的构造方法能够传入一个路径(可以是绝对路径和相对路径)来指定一个文件:

//指定文件, 文件路径为 d:/test.txt
File file = new File("d:/test.txt");

注意:在Java语言中, 反斜杠"“表示转义字符, 如果将文件路径写成这样的形式:“d:\test.txt”, 编译器会先识别”\t", 因此, 可以将路径中的反斜杠"\" 替换为"/", 就不会出现路径与转义字符产生冲突的情况了.

常见的File类的操作有:
JavaEE初阶 - 文件操作和IO_第2张图片

例如:
对一个已存在的文件进行操作:

//指定一个存在的文件
File file = new File("d:/test.txt");
//getName方法, 打印文件名称(不打印路径)
System.out.println(file.getName());//结果:test.txt
//getParent方法, 返回父目录的文件路径
System.out.println(file.getParent());//结果:d:\
//exists方法,判断当前指定的文件是否真实存在
System.out.println(file.exists());//结果:true
//getAbsoluteFile方法, 获取到当前文件夹的绝对路径
System.out.println(file.getAbsoluteFile());//结果:d:\test.txt
//isFile方法, 判断当前文件是否为一个普通文件(不是一个目录)
System.out.println(file.isFile());//结果:true        

对一个不存在的文件进行操作:

// "d:/"路径下不存在test2.txt文件
File file2 = new File("d:/test2.txt");
//判断当前文件是否存在
System.out.println(file2.exists());//结果:false
//如果当前指定的文件不存在, 则创建出一个这样的文件, 创建成功返回true, 失败返回false
System.out.println(file2.createNewFile());//结果:true
//已经创建好了这个文件, 再创建一次, 会创建失败
System.out.println(file2.createNewFile());//结果:false
//删除当前指定的文件, 成功返回true, 失败返回false
System.out.println(file2.delete());//结果:true

6. 文件内容的操作

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

  1. 字节流对象, 针对二进制文件, 以字节为单位进行读写
    读操作:InputStream
    写操作:OutputStream
  2. 字符流对象, 针对文本文件, 以字符为单位进行读写
    读:Reader
    写:Writer

这四个类都是抽象类, 因此一般实例化的是这些类的子类, 分别为FileInputStream, FileOutputStream, FileReader, FileWriter

InputStream和OutputStream

使用InputStream每次读取一个字符:

public static void main(String[] args) {
    //注意:
    //try后面的括号里必须定义一个实现了Closeable接口的类的变量
    //所有的流对象都实现了Closeable接口
    try (InputStream inputStream = new FileInputStream("d:/test.txt")){
        while (true){
            int b = inputStream.read();
            //read()方法返回-1代表读取到了文件末尾
            if(b==-1)   break;
            System.out.println(b);
        }
    }catch (IOException e){
        e.getStackTrace();
    }
    //在这个代码中, 不需要显式地调用close(), try会帮我们自动调用
    //当try语句块执行完后, 就会自动调用close()
}

  这种操作每次只读一个字符, 而读取磁盘的速度非常慢, 多次读取磁盘的效率非常低下, 下面这种写法可以实现一次读取多个字符.

public static void main(String[] args) {
    try (InputStream inputStream = new FileInputStream("d:/test.txt")){
        byte[] bytes = new byte[1024];
        while (true){
            //注意:如果read()方法不带参数的话, 返回的是读到的数值
            //read()方法中一旦有参数, 返回的就是读取到的字符的个数了
            //而字符被存放在了数组里(这个数组就是调用read方法时传入的数组)
            int b = inputStream.read(bytes);
            //read()方法返回-1代表读取到了文件末尾
            if(b==-1)   break;
            for(int i=0;i<b;i++){
                System.out.println(bytes[i]);
            }
        }
    }catch (IOException e){
        e.printStackTrace();
    }
}

使用OutputStream:

public static void main(String[] args) {
    try(OutputStream outputStream = new FileOutputStream("d:/test.txt")){
        //每次写入一个字符
        //注意:此时这里写入的是ascii值, 并没有写入整数
        //例如a的ascii值为97, 那么参数为97时写入的就是a
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);

        //写入整个数组中的内容
        byte[] bytes = new byte[]{97,98,99};
        for (int i=0;i< bytes.length;++i){
            outputStream.write(bytes[i]);
        }

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

注意:这种写入方式会先删除文件内的所有内容, 然后再写入, 下面会提到如何不删除文件内容, 直接在文件末尾写入

Reader和Writer

使用Reader:

public static void main(String[] args) {
    try(Reader reader = new FileReader("d:/test.txt")){
        //按照字符方式来读
        char[] chars = new char[1024];
        while (true){
            //len表示实际读到的字符个数
            int len = reader.read(chars);
            //len = -1,代表读取到了文件末尾
            if (len == -1)  break;
            //这种方式是直接打印数组元素
            for (int i=0;i<len;++i){
                System.out.println(chars[i]);
            }
            //这种方式是将数组元素转换为字符串进行打印
            String s = new String(chars,0,len);
            System.out.println(s);
        }
    }catch (IOException e){
        e.printStackTrace();
    }
}

使用Writer:

//使用字符流写入文件
public class Demo7 {
    public static void main(String[] args) {
        try(Writer writer = new FileWriter("d:/test.txt")){
            char[] chars = new char[]{97,98,99};
            //写入一个字符数组, 就直接将字符数组里的所有内容写入文件中
            writer.write(chars);
            //我们也可以直接写入一个字符串
            writer.write("abc");
            //写入一个字符串, 从字符串的off位置开始写入(off=0,也就是a位置),写入的长度为len
            writer.write("abcdef",0,3); //这条语句的写入结果就是"abc"
            //形参给一个整数, write()就会写入这个整数对应的字符
            //例如给一个97, write就会写入a
            writer.write(97);
        }catch (IOException e){
            e.printStackTrace();
        }
        //创建一个线程来使用字符流读取文件
        Thread thread = new Thread(()->{
            try (Reader reader = new FileReader("d:/test.txt")){
                char[] chars = new char[1024];
                while (true){
                    int len = reader.read(chars);
                    if (len == -1)  break;
                    String s = new String(chars,0,len);
                    System.out.println(s);
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        });
        thread.start();
    }
}
//结果:
abcabcabca

  如果想写入时不删除文件内容, 而是直接在文件末尾写入, 我们只需在创建对象时加上一个参数true即可(字节流和字符流同理):

OutputStream outputStream = new FileOutputStream("d:/test.txt",true);
Writer writer = new FileWriter("d:/test.txt",true);

7. 文件操作的案例

删除指定文件

import java.io.File;
import java.util.Scanner;
public class Demo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入文件路径:");
        String rootDirPath = scanner.next();
        System.out.println("输入要删除的文件名:");
        String toDeleteFile = scanner.next();
        File file = new File(rootDirPath);
        //输入的内容不是一个路径
        if(!file.isDirectory()){
            System.out.println("输入有误!");
            return;
        }
        deleteFile(file,toDeleteFile);

    }
    //通过递归遍历文件列表, 找到并删除指定名称的文件
    public static void deleteFile(File file, String toDeleteFile){
        File[] files = file.listFiles();
        if(files == null){
            return ;
        }
        //遍历文件列表, 如果查询到文件, 判断是否需要删除
        //如果是目录, 递归遍历
        for(int i=0;i< files.length;++i){
            //如果遍历到文件
            if(files[i].isFile()){
                //当前遍历到的文件名与要删除的文件名相等
                if(files[i].getName().equals(toDeleteFile)){
                    //删除文件
                    files[i].delete();
                }
            }
            //遍历到目录,递归遍历
            else if(files[i].isDirectory()){
                deleteFile(files[i],toDeleteFile);
            }
        }
    }
}

注意:这里删除文件时路径不要设置的太大, 比如在D盘中找一个普通文件, 这样可能会导致程序很长时间找不到要删除的文件, 最好的做法是输入一个文件夹, 这样程序可以快速地找到指定的文件并删除.

复制文件内容

用户输入两个文件, 一个是源文件(被复制的文件), 一个是目标文件

import java.io.*;
import java.util.Scanner;
public class Demo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //被复制的文件
        System.out.println("请输入需要复制的文件路径");
        String src = scanner.next();
        //要复制生成的文件
        System.out.println("请输入要复制生成的文件路径:");
        String dest = scanner.next();
        File file = new File(src);
        //输入的不是文件
        if(!file.isFile()){
            System.out.println("输入有误!");
            return;
        }
        //不需要检查目标文件是否存在, OutputStream会自动创建不存在的文件
        //读取文件
        try (InputStream inputStream = new FileInputStream(src)){
            //写入文件
            try (OutputStream outputStream = new FileOutputStream(dest)){
                //将inputStream中的文件读出并写入到outputStream中
                byte[] bytes = new byte[1024];
                while (true){
                    int len = inputStream.read(bytes);
                    if(len == -1)   break;
                    //将读取出的字节写入
                    outputStream.write(bytes,0,len);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(JavaEE初阶,java-ee,java)