文件操作IO

认识文件


1. 文件和目录

文件就是存储在硬盘中的数据集合。

文件除了数据以外,还有文件名,文件类型,文件大小等信息。

在这里插入图片描述

目录通俗的讲就是文件夹,目录是专门用来存放管理信息的特殊文件。

目录是有层级结构的,也就是树型结构。

在这里插入图片描述


2. 文件路径

路径表示一个 文件/目录 的具体位置。

Windows系统中 一个路径 对应一个 文件/目录。

绝对路径

从根结点开始,一路往下找到目标文件/目录,中间经过的目录就是绝对路径了

在这里插入图片描述

路径中,每个目录用 \/ 来分割,\ 只在Windows中适用,在代码中要写成 \\/

相对路径

相对路径需要明确一个基准目录(工作目录)

假设基准目录为:C:/Test/123/hello

一个文件的绝对路径为:C:/Test/123/test.java

那么这个的相对路径为:../test.java

../ 表示当前目录(基准目录)的上一层

../../ 表示当前目录(基准目录)的上上层

./ 表示在当前目录


3. 文件分类

文件分为 文本文件二进制文件

文本文件存储的数据,遵守 ASCII码 或者 UTF-8等字符集编码。

.txt .java .c 都是文本文件

二进制文件存储的是二进制数据。

.class .exe .jpg .mp3 等都是二进制文件。

一个简单的区分方法:

将一个文件拖入记事本中,如果看得懂,就是文本文件。

如果看不懂,都是乱码,就是二进制文件。

拖入了一个 .xls

在这里插入图片描述


文件系统操作

文件系统操作就是创建文件,删除文件,创建目录,重命名等操作,不涉及文件内容的读写操作。

Java 中 java.io.File 类就是对一个文件进行抽象,创建这个类的实例,就可以对文件系统进行操作。


1. File 类的属性

static StringpathSeparator依赖于系统的路径分隔符,String 类型的表示
static charpathSeparatorChar依赖于系统的路径分隔符,char 类型的表示

在这里插入图片描述

在这里插入图片描述


2. File 类的构造方法

在这里插入图片描述

注意: File 实例, 不能代表该文件真实存在。

示例:

在 D:/Test_IO 目录下使用这三个构造方法创建文件, 开始这个目录没有文件。

在这里插入图片描述

代码:

public static void main(String[] args) throws IOException {
    // 根据路径创建一个File实例, 绝对路径或者相对路径都可以
    File file1 = new File("D:/Test_IO/test1.txt");

    File directory = new File("D:/Test_IO");

    // 父目录 + 孩子文件路径, 父目录是一个 File 对象, 而且必须是目录
    File file2 = new File(directory, "test2.txt");

    // 父目录 + 孩子文件路径, 父目录用路径表示
    File file3 = new File("D:/Test_IO/Test", "../test3.txt");

    // exists  文件是否真实存在
    System.out.println(file1.exists());
    System.out.println(file2.exists());
    System.out.println(file3.exists());

    // createNewFile 根据 File 对象, 创建一个空文件
    file1.createNewFile();
    file2.createNewFile();
    file3.createNewFile();

    System.out.println(file1.exists());
    System.out.println(file2.exists());
    System.out.println(file3.exists());
}

结果:

在这里插入图片描述

成功创建了三个文件

在这里插入图片描述


3. File 类常用方法

3.1 get方法

返回值类型方法说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径

代码示例:

public static void main(String[] args) throws IOException {
    // 使用相对路径
    File file1 = new File("../test.txt"); // 不要求文件真实存在
    System.out.println(file1.getParent()); // 返回 File 对象的父目录文件路径
    System.out.println(file1.getName()); // 返回 File 对象的文件名称
    System.out.println(file1.getPath()); // 返回 File 对象的文件路径, 就是构造时传入的参数
    System.out.println(file1.getAbsolutePath()); // 返回 File 对象的绝对路径
    System.out.println(file1.getCanonicalPath()); // 返回 File 对象的修饰过的绝对路径

    System.out.println();

    // 使用绝对路径
    File file2 = new File("D:/Test_IO/test.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());
}

结果:

在这里插入图片描述


3.2 创建、删除普通文件

返回值类型方法说明
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行

代码示例:

public static void main(String[] args) throws IOException {
    File file = new File("D:/Test_IO/test.txt");

    System.out.println("是否存在: " + file.exists());
    System.out.println("是否是目录: " + file.isDirectory());
    System.out.println("是否是普通文件: " + file.isFile() + '\n');

    System.out.println("创建文件: " + file.createNewFile() + '\n');

    System.out.println("是否存在: " + file.exists());
    System.out.println("是否是目录: " + file.isDirectory());
    System.out.println("是否是普通文件: " + file.isFile() + '\n');

    System.out.println("删除文件: " + file.delete() + "\n");

    System.out.println("是否存在: " + file.exists());
    System.out.println("创建文件: " + file.createNewFile() + '\n');

    file.deleteOnExit();
    System.out.println("deleteOnExit()");

    System.out.println("是否存在: " + file.exists());
}

结果:

在这里插入图片描述

注意事项:deleteOnExit() 在运行结束才会删除文件。


3.3 创建目录

返回值类型方法说明
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录

创建一级目录:

public static void main(String[] args) {
    File directory = new File("D:/Test_IO/Test");
    System.out.println("是否存在: " + directory.exists());
    System.out.println("是否是目录: " + directory.isDirectory());
    System.out.println("是否是普通文件: " + directory.isFile() + '\n');

    System.out.println("创建一级目录: " + directory.mkdir() + '\n');

    System.out.println("是否存在: " + directory.exists());
    System.out.println("是否是目录: " + directory.isDirectory());
    System.out.println("是否是普通文件: " + directory.isFile() + '\n');
}

结果:

在这里插入图片描述

在这里插入图片描述

创建多级目录:

public static void main(String[] args) {
    File directory = new File("D:/Test_IO/Test/Hello");
    System.out.println("是否存在: " + directory.exists());
    System.out.println("是否是目录: " + directory.isDirectory());
    System.out.println("是否是普通文件: " + directory.isFile() + '\n');

    System.out.println("创建多级目录: " + directory.mkdirs() + '\n');

    System.out.println("是否存在: " + directory.exists());
    System.out.println("是否是目录: " + directory.isDirectory());
    System.out.println("是否是普通文件: " + directory.isFile() + '\n');
}

结果:

在这里插入图片描述

在这里插入图片描述

注意:创建一级目录可以用 mkdir()mkdirs() , 创建多级目录必须用 mkdirs(), 目录的删除与普通文件一致


3.4 文件重命名

返回值类型方法说明
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作

示例:

在这里插入图片描述

public static void main(String[] args) {
    File file = new File("D:/Test_IO/test.txt"); // 要求 test.txt 得存在,可以是普通文件,可以是目录
    File dest = new File("hello.txt"); // 要求 hello.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
}

结果:

在这里插入图片描述
在这里插入图片描述


3.5 其他操作

返回值类型方法说明
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示

文件内容操作

对文件内容进行读取和写入。


一、字节流

1. 读取文件


InputStream
修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数 量;-1 代表以及读完了
intread(byte[] b, int off, int len)最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返 回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

InputStream 是一个抽象类, 要搭配具体的实现类,我们要对文件操作,所以搭配 FileInputStream 使用

FileInputStream
构造方法说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流

代码示例

在这里插入图片描述

public static void main(String[] args) throws IOException {
    // 相当于打开文件
    // InputStream 是抽象类
    // FileInputStream 构造文件输入流
    // 文件必须存在
    try (InputStream inputStream = new FileInputStream("D:/Test_IO/test.txt")) {
        while (true) {
            // read 一次读取一个字节, 但是 read 的返回值是 int
            int b = inputStream.read(); // 读取一个字节的数据,返回 -1 代表已经完全读完了
            if (b == -1) {
                break;
            }
            char ch = (char)b;
            System.out.print(ch);
        }
    }
    // 使用 try(){} 这个写法会自动关闭字节流
    // inputStream.close();
}

在这里插入图片描述

public static void main(String[] args){
    try (InputStream inputStream = new FileInputStream("D:/Test_IO/test.txt")) {
        byte[] buf = new byte[1024];
        int len;
        while (true) {
            len = inputStream.read(buf); // 最多读取 b.length 字节的数据到 b 中, 返回实际读到的数量. -1 代表以及读完了
            // 一次最多读1024个字节,尽量填满数组
            // 读到末尾返回 -1
            if (len == -1) {
                break;
            }
            for (int i = 0; i < len; i++) {
                System.out.print((char)buf[i]);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

在这里插入图片描述


搭配 Scanner 可以读取字符
构造方法说明
Scanner(InputStream is, String charset)使用 charset 字符集进行 is 的扫描读取

代码示例:

public static void main(String[] args) {
    try (InputStream inputStream = new FileInputStream("D:/Test_IO/test.txt")) {
        try (Scanner scanner = new Scanner(inputStream, "UTF-8")) {
            while (scanner.hasNext()) {
                String s = scanner.nextLine();
                System.out.println(s);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

在这里插入图片描述


2. 写入文件

OutputStream
修饰符及返回值类型方法签名说明
voidwrite(int b)写入要给字节的数据
voidwrite(byte[] b)将 b 这个字符数组中的数据全部写入 os 中
intwrite(byte[] b, int off, int len)将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
voidclose()关闭字节流
voidflush()重要:OutputStream 为提高效率,会将数据暂时放在内存中, 等到数据足够多了,才会真正的将数据写入硬盘,调用 flush(刷新)操作,将数据写入硬盘。

OutputStream 也是一个抽象类, 要搭配具体的实现类,我们要对文件操作,所以搭配 FileOutputStream 使用

FileOutputStream
构造方法说明
FileOutputStream(File file)利用 File 构造文件输出流
FileOutputStream(String name)利用文件路径构造文件输出流

代码示例
public static void main(String[] args) {
    // 文件可以不存在, 会自动创建
    try (OutputStream outputStream = new FileOutputStream("D:/Test_IO/output.txt")) {
        outputStream.write('H');// 写入一个字节
        outputStream.write('e');
        outputStream.write('l');
        outputStream.write('l');
        outputStream.write('o');

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

结果:

在这里插入图片描述

写入中文字符

public static void main(String[] args) throws IOException {
    try (OutputStream os = new FileOutputStream("D:/Test_IO/output.txt")) {
        String s = "你好";
        byte[] b = s.getBytes("utf-8");

        os.write(b);

        os.flush();
    }
}

结果:

在这里插入图片描述


搭配 PrintWriter 进行字符写入

代码示例:

public static void main(String[] args) {
    try (OutputStream os = new FileOutputStream("D:/Test_IO/output.txt")) {
        try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os,"utf-8")) {
            try (PrintWriter printWriter = new PrintWriter(outputStreamWriter)) {
                printWriter.println("hello");
                printWriter.print("你好\r\n");
                printWriter.printf("1 + 1 = %d\r\n", 1 + 1);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

在这里插入图片描述


二、字符流

1. 读取文件

Reader

Reader 抽象类,读取文件需要搭配 FileReader 使用

在这里插入图片描述

代码示例:

public static void main(String[] args) {
    // 文件必须存在
    try (Reader reader = new FileReader("D:/Test_IO/test.txt")) {
        while (true) {
            int c = reader.read();
            if (c == -1) {
                break;
            }
            System.out.print((char)c);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

在这里插入图片描述


2. 写入文件

Writer

Writer 抽象类,读取文件需要搭配 FileWriter 使用

代码示例:

public static void main(String[] args) {
    try (Writer writer = new FileWriter("D:/Test_IO/output.txt")) {
        writer.write('a');
        writer.write('b');
        writer.write('\n');
        writer.write('你');
        writer.write('好');
        writer.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

结果:

在这里插入图片描述


练习案例

1. 文件名查找,删除文件

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

代码:

public class IODemo12 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要扫描的根目录 (绝对路径 或 相对路径): ");
        String rootDirPath = scanner.next();

        // 创建 File 实例
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            // 不是目录, 或者不存在
            System.out.println("您输入的根目录不存在或者不是目录! ");
            return;
        }

        System.out.print("请输入要找出的文件名中的字符: ");
        String token = scanner.next();
		
        // 存放结果
        List<File> result = new ArrayList<>();
		
        // 递归扫描
        scanDir(rootDir, token, result);

        System.out.println("共找到了符合条件的文件" + result.size() + "个, 分别是");
        for (File file : result) {
            System.out.println(file.getCanonicalPath() + "   您是否要删除改文件? y/n");
            String in = scanner.next();
            if (in.toLowerCase().equals("y")) {
                // 输出文件
                file.delete();
            }
        }
    }

    // 递归遍历
    private static void scanDir(File rootDir, String token, List<File> result) {
        File[] files = rootDir.listFiles();// 获取当前目录下的所有文件
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                // 是目录
                scanDir(file, token, result);
            } else if (file.isFile()) {
                // 是普通文件
                if (file.getName().contains(token)) {
                    // 文件名包含指定字符
                    result.add(file);
                }
            } else {
                // 不是目录和普通文件, 跳过
                continue;
            }
        }
    }
}

演示:

在这里插入图片描述


2. 普通文件复制

进行普通文件复制

代码:

public class IODemo13 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要复制的文件 (绝对路径 或 相对路径): ");
        String sourcePath = scanner.next();

        // 创建 File 实例
        File sourceFile = new File(sourcePath);
        if (!sourceFile.exists()) {
            System.out.println("文件不存在, 请确认路径是否正确!");
            return;
        }
        if (!sourceFile.isFile()) {
            // 不是普通文件, 或者不存在
            System.out.println("文件不是普通文件, 请确认路径是否正确!");
            return;
        }

        System.out.print("请输入要复制的目标路径 (绝对路径 或 相对路径): ");
        String destPath = scanner.next();
        
        File destFile = new File(destPath);
        if (destFile.exists()) {
            // 目标存在
            if (destFile.isFile()) {
                System.out.println("目标路径已经存在, 是否要进行覆盖?y/n");
                String ans = scanner.next();
                if (!ans.toLowerCase().equals("y")) {
                    System.out.println("停止复制");
                    return;
                }
            } else {
                System.out.println("目标路径已存在, 并且不是普通文件, 请确认路径是否正确!");
            }
        }
		
        
        try (InputStream inputStream = new FileInputStream(sourceFile)) {
            try (OutputStream outputStream = new FileOutputStream(destFile)) {
                while (true) {
                    byte[] b = new byte[1024];
                    int len = inputStream.read(b);// 读取
                    if (len == -1) {
                        break;
                    }
                    outputStream.write(b, 0, len);// 写入
                    outputStream.flush();
                }
            }
        }
        System.out.println("复制已完成");
    }
}

演示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


3. 文件名和内容中查找

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

这个方案性能较差,不要在大文件下扫描,可能会内存不够

代码:

public class IODemo14 {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入要扫描的根目录(绝对路径 或 相对路径):");
        String rootDirPath = scanner.next();
        File rootDir = new File(rootDirPath);
        if (!rootDir.isDirectory()) {
            System.out.println("您输入的根目录不存在或不是目录!");
            return;
        }

        System.out.print("请输入要找的字符:");

        String token = scanner.next();
		
        // 存放结果
        List<File> result = new ArrayList<>();
		
        // 递归
        scanDirWithContent(rootDir, token, result);

        System.out.println("以下 " + result.size() + " 个文件包含 \'" + token +"\':");
		
        // 输出结果
        for (File file : result) {
            System.out.println(file.getCanonicalPath());
        }
    }
    private static void scanDirWithContent(File rootDir, String token, List<File> result) {
        // 获取当前目录下的所有文件
        File[] files = rootDir.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            System.out.println("当前搜索到:" + file.getAbsolutePath());
            if (file.isDirectory()) {
                // 是目录
                scanDirWithContent(file, token, result);
            } else if (file.isFile()) {
                // 是普通文件
                if (file.getName().contains(token)) {
                    // 文件名是否包含
                    result.add(file);
                    continue;
                }
                if (isContentContains(file, token)) {
                    // 文件内容
                    result.add(file);
                }
            } else {
                // 不是目录和普通文件, 跳过
                continue;
            }
        }
    }
    // 使用字节流 搭配 Scanner
    private static boolean isContentContains(File file, String token) {
        StringBuilder stringBuilder = new StringBuilder();
        try (InputStream inputStream = new FileInputStream(file)) {
            try (Scanner scanner = new Scanner(inputStream, "utf-8")) {
                while (scanner.hasNextLine()) {
                    // 一次读一行
                    stringBuilder.append(scanner.nextLine());
                    stringBuilder.append("\r\n");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return stringBuilder.indexOf(token) != -1;
    }
}

演示:

在这里插入图片描述


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值