【Java】文件的操作与输入输出流

  • 文件
  • Java操作文件
    • File类
      • 属性
      • 构造方法
      • 常用方法
    • 文件内容的读写-数据流
      • 字节流(处理二进制文件)
        • InputStream和FileInputStream
        • OutputStream和FileOutputStream
      • 字符流(处理文本文件)
        • Reader和FileReader
        • Writer和FileWriter
      • 其他方法
        • Scanner(通过输入流读)
        • PrintWriter(通过输出流写)
  • 练习

文件

1.狭义上的文件
硬盘上保存的数据,都是“文件”来组织的,本质上都是二进制或是字符组织的数组,被打包成一个文件存在硬盘上。常见的文件有图片,文本,可执行文件,音频,视频…文件夹也是一种特殊的文件,也叫目录通常所说的文件都是存储在硬盘上面的,硬盘的特点:1硬盘容量大,内存容量小。2.硬盘读写速度慢,内存读写速度快。3.硬盘造价低,内存成本比较高。4.硬盘上的数据断电不丢失,内存中的数据断电丢失。
2..广义上的文件
操作系统的主要功能就是对计算机资源进行统一管理与分配。对于Linux来讲,所有的计算设备(网卡、键盘、打印机…)都会被描述(抽象)成文件。当一个进程启动后去申请计算机资源时,系统会把他所有用到的资源以文件的形式分配给进程,并加入到对应的文件描述符表中。
3.树形结构和目录
通过 tree /F在命令行查看指定目录的树形结构。

4.绝对路径
从根目录开始一直到目标程序的表示方式。

5.相对路径
从当前目录开始表示目标程序路径的方式。如果要通过相对路径的方式访问到目标文件,那么就得先确认自己当前的工作目录。对于ideal来说,起始工作目录就是工程的根目录。

⚠️注意
平时使用的时候可以使用绝对,工作和项目中尽量使用相对路径。

Java操作文件

操作系统的一个重要功能就是对文件的管理,每个操作系统都有自己的一套系统API调用,Java作为一个跨平台的语言,JVM针对不同的操作系统做了一层封装,我们只需要使用JDK提供的关于文件操作的API就可以完成不同系统上的文件操作。

File类

用来操作文件的类,位于java.io包下。I:input,O: output。输入输出以内存为参照物的。输入指的是从外部输入到内存,输出指的是把内存的数据输出外部(磁盘)。

属性

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

构造方法

签名说明
File(File parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径
File(String parent, String child)根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示
public class Demo01_File {
    public static void main(String[] args) {
        //通过指定文件路径来创建一个File对象
        File file = new File("D:\\test\\hello.txt");
        System.out.println(file);
        File file1 = new File("D:/test/hello.txt");
        System.out.println(file1);
        //这只是java层面的一个对象,并不一定必须在系统中真实存在
        File file2 = new File("D:/test/test.txt");
        System.out.println(file2);
    }
}

注意斜杠与反斜杠的区别,反斜杠需要转义。

常用方法

修饰符及返回值类型方法签名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
booleanrenameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限
public class Demo02_FileUsae {
    public static void main(String[] args) throws IOException {
        //指定绝对路径来创建一个File对象
        File file = new File("D:/test/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.exists());
        // 是不是一个目录
        System.out.println(file.isDirectory());
        // 是不是一个文件
        System.out.println(file.isFile());
//
//        // 创建文件
//        boolean result1 = file.createNewFile();
//        if (result1) {
//            System.out.println("创建成功");
//        } else {
//            System.out.println("创建失败");
//        }
//
//        // 删除
//        boolean result2 = file.delete();
//        if (result2) {
//            System.out.println("删除成功");
//        } else {
//            System.out.println("删除失败");
//        }

//        // 指定一个目录的路径
//        File file = new File("d:/test");
//        // 获取目录下的文件和子目录
//        String[] list = file.list();
//        System.out.println(Arrays.toString(list));
//
//        // 获取目录下的文件对象数组
//        File[] files = file.listFiles();
//        System.out.println(Arrays.toString(files));

//        // 指定要创建的目录
//        File file = new File("D:/test/java");
//        // 创建单个目录
//        boolean result = file.mkdir();
//        if (result) {
//            System.out.println("创建成功");
//        } else {
//            System.out.println("创建失败");
//        }
//        // 指定要创建的目录
//        File file = new File("D:/test/java/a/b/c");
//        // 创建单个目录
//        boolean result = file.mkdirs();
//        if (result) {
//            System.out.println("创建成功");
//        } else {
//            System.out.println("创建失败");
//        }
//        // 定义源文件
//        File sourceFile = new File("D:/test/hello.txt");
//        // 定义目标文件
//        File destFile = new File("D:/test/haha.txt");
//        // 重命名
//        boolean result = sourceFile.renameTo(destFile);
//        if (result) {
//            System.out.println("修改成功");
//        } else {
//            System.out.println("修改失败");
//        }
//
//        File file = new File("D:/test/haha.txt");
//        // 是否可写
//        System.out.println(file.canWrite());
//        // 是否可读
//        System.out.println(file.canRead());



//        //指定相对路径来创建一个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.exists());
//        // 是不是一个目录
//        System.out.println(file.isDirectory());
//        // 是不是一个文件
//        System.out.println(file.isFile());
    }
}

文件内容的读写-数据流


字节流(处理二进制文件)

InputStream和FileInputStream

🟢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 class Demo03_InputStream_Read01 {
    public static void main(String[] args) throws IOException {
        // 创建一个文件对象
        File file = new File("d:/test/haha.txt");
        // 创建一个输入流
        InputStream inputStream = new FileInputStream(file);
        // 读取文件内容
        while (true) {
            int read = inputStream.read();
            // 是否读完
            if (read == -1) {
                break;
            }
            System.out.println(read);
        }
        // 关闭
        inputStream.close();
    }
}

示例:用一个数组用来保存读取到的数据,遍历数组即可读取到数据。

public class Demo04_InputStream_Read02 {
    public static void main(String[] args) throws IOException {
        // 创建一个文件对象
        File file = new File("d:/test/haha.txt");
        // 创建一个输入流
        InputStream inputStream = new FileInputStream(file);
        // 定义一个数组用来保存读取到的数据
        byte[] bytes = new byte[1024];
        // 读取文件内容
        while (true) {
            // 读到的数据会被填充到bytes数据中,返回读取数据的长度
            int len = inputStream.read(bytes);
            // 判断是否读完
            if (len == -1) {
                break;
            }
            // 打印读到的内容
            for (int i = 0; i < len; i++) {
                System.out.println(bytes[i]);
            }
        }
        // 关闭
        inputStream.close();
    }
}

在这个示例中,传入read()方法的是一个空数组,读文件时,读数组长度个字节,并返回读取到的字节数供调用方做判断
打开一个文件相当于把文件放入文件描述符表中,本质上是一个数组。用完一定要关闭,如果不关闭,文件描述符表就会被填满,导致以后可能无法再打开文件。

OutputStream和FileOutputStream

OutputStream和FileOutputStream分别作为字节流中输出流的抽象类和实现类。

🟢OutputStream的方法

修饰符及返回值类型方法说明
viodwrite(int b)将数据写入指定的文件
viodwrite(byte[] b)将 b 这个字符数组中的数据全部写入 os 中
intwrite(byte[] b, int off,int len)将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
viodclose()关闭字节流
viodflush()重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。
public class Demo05_OutputStream {
    public static void main(String[] args) throws IOException {
        // 先创建一个File对象
        File file = new File("d:/test/haha.txt");
        // 根据File对象创建一个输出流
        FileOutputStream outputStream = new FileOutputStream(file);
        // 向文件中写入内容
        outputStream.write(100);
        outputStream.write(101);
        outputStream.write(102);
        // 刷新缓冲区
        outputStream.flush();
        // 关闭流
        outputStream.close();
    }
}

调用write()方法,就表示通过输出流把内容写到指定的文件中。
缓冲区本身是内存中的一片区域,写的文件内容一般是先写到缓冲区中,缓冲区的内容什么时候写到文件中是由操作系统决定的。如果缓冲区的内容还没写满就要强制写入文件时,可以使用flush()方法。
在完成写操作之后,建议强制调用flush()方法刷新缓冲区,确保文件内容被立即写入
用输出流的方式去写文件内容,会把之前的内容全部覆盖掉。

字符流(处理文本文件)

Reader和FileReader

Reader和FileReader分别是字符流中输入流的抽象类和实现类。

public class Demo06_FileReader {
    public static void main(String[] args) throws IOException {
        // 创建一个File对象
        File file = new File("d:/test/haha.txt");
        // 根据File对象创建一个Reader(面向字符的输入流)
        FileReader reader = new FileReader(file);
        // 循环读取
        while (true) {
            // 一次读一个字符
            int read = reader.read();
            if (read == -1) {
                break;
            }
            // 打印
            System.out.println((char) read);
        }
        // 关闭流
        reader.close();
    }
}

read()方法每次只读一个字符。

Writer和FileWriter

Writer和FileWriter分别是字符流中输出流的抽象类和实现类。

public class Demo07_FileWriter {
    public static void main(String[] args) throws IOException {
        // 创建一个File对象
        File file = new File("d:/test/haha.txt");
        // 根据文件对象创建一个字符输出流
        FileWriter writer = new FileWriter(file);
        // 写入内容
        writer.write("你好世界\n");
        writer.write("hello world");
        // 强制刷新缓冲区
        writer.flush();
        // 关闭流
        writer.close();

    }
}

write()方法每次写入一个字符串,写入时不会自动换行,需要换行时要手动添加换行符。
这里是引用

其他方法

Scanner(通过输入流读)
public class Demo08_Scanner {
    public static void main(String[] args) throws IOException {
        // 创建一个输入流
        FileInputStream inputStream = new FileInputStream("d:/test/hello.txt");
        // 根据创建输入流创建Scanner对象
        Scanner scanner = new Scanner(inputStream, "UTF-8");
        // 循环读取内容
        while (scanner.hasNextLine()) {
            String next = scanner.nextLine();
            System.out.println(next);
        }
        scanner.close();
        inputStream.close();
    }
}

读数据时,调用nextLine()方法读取一行。

PrintWriter(通过输出流写)
public class Demo09_PrintWiter {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建一个输出流
        FileOutputStream outputStream = new FileOutputStream("d:/test/hello.txt");
        // 根据输出流,创建一个PrintWriter
        PrintWriter printWriter = new PrintWriter(outputStream);
        // 写入文件
        printWriter.println("你好世界!!!");
        printWriter.println("hello world.");
        printWriter.println("我是用PrintWriter写入的内容");
        // 强制刷新缓冲区
        printWriter.flush();
        // 关闭流
        printWriter.close();
    }
}

写入字符串时,调用 println()方法会自动换行。

练习

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

1.用户输入一个路径;
2.检查路径是否有效;
3.用户输入目标字符;
4.获取路径下的文件和目录;
5.如果是目录,递归;
6.如果是文件检查文件名是否包含要用户输入的字符。

public class Ex01 {
    public static void main(String[] args) throws IOException {
        // 1. 接收用户输入的扫描路径
        System.out.println("请输入要扫描的路径(绝对路径):");
        Scanner scanner = new Scanner(System.in);
        String rootPath = scanner.next();
        // 2. 判断路径是否有效
        File root = new File(rootPath);
        // 2.1 路径是否存在
        if (!root.exists()) {
            System.out.println("路径不存在");
            return;
        }
        // 2.2 判断File是不是一个目录
        if (!root.isDirectory()) {
            System.out.println("指定的路径不是一个有效目录");
            return;
        }
        // 3. 接收关键字
        System.out.println("请输入关键字");
        String key = scanner.next();
        if (key == null || "".equals(key)) {
            System.out.println("关键字不能为为");
            return;
        }
        // 4. 扫描目录下的所有文件
        scan(root, key);
    }

    private static void scan(File root, String key) throws IOException {
        // 1. 先获取root下的所有文件,包括目录
        File[] files = root.listFiles();
        // 递归的终止条件
        if (files == null || files.length == 0) {
            return;
        }
        // 遍历数组中的每个文件
        for (int i = 0; i < files.length; i++) {
            // 取出每一个文件
            File tempFile = files[i];
            // 判断是文件还是目录
            if (tempFile.isFile()) {
                // 如果是文件,判断文件名中是否包含关键字
                String fileName = tempFile.getName();
                // 如果在文件名中找到关键字
                if (fileName.contains(key)) {
                    System.out.println("找到文件:" + tempFile.getCanonicalPath() + ", 是否删除(Y/N)");
                    // 接收用户的输入,根据输入判断是否删除
                    Scanner scanner = new Scanner(System.in);
                    String order = scanner.next();
                    // 删除操作
                    if (order.equalsIgnoreCase("y")) {
                        tempFile.delete();
                        System.out.println(tempFile.getCanonicalPath() + " 删除成功.");
                    }
                }
            } else {
                // 如果是目录则递归
                scan(tempFile, key);
            }
        }
    }
}

2.对普通文件进行复制

1.用户输入源文件的路径;
2.检查源文件是否存在,并且是一个文件;
3.用户输入目标文件的路径;
4.检查目标文件是否存在,并校验目录或文件;
5.完成复制。

public class Ex02 {
    public static void main(String[] args) {
        // 1 . 接收用户输入的源文件路径
        System.out.println("请输入源文件路径(绝对路径):");
        Scanner scanner = new Scanner(System.in);
        String sourcePath = scanner.next();
        // 2. 判断源文件路径是否有效
        File sourceFile = new File(sourcePath);
        // 2.1 文件是否存在
        if (!sourceFile.exists()) {
            System.out.println("源文件不存在");
            return;
        }
        // 2.2 判断是不是一个文件
        if (!sourceFile.isFile()) {
            System.out.println("源文件不是一个有效的文件");
            return;
        }
        // 3. 接收用户输入的目标文件路径
        System.out.println("请输入目标文件路径(绝对路径)");
        String destPath = scanner.next();
        File destFile = new File(destPath);
        // 3.1 判断目标文件是否存在
        if (destFile.exists()) {
            System.out.println("目标文件已存在");
            return;
        }
        // 3.2 判断目标文件的父目录是否存在
        if (!destFile.getParentFile().exists()) {
            System.out.println("目标文件的父目录不存在");
            return;
        }
        // 循环读取源文件的内容并写到目标文件中
        try (FileInputStream inputStream = new FileInputStream(sourceFile);
             FileOutputStream outputStream = new FileOutputStream(destFile)) {
            // 定义一个byte数组用来做为输出型参数,保存每次读到的文件内容
            byte [] bytes = new byte[1024];
            // 循环读取内容
            while (true) {
                int len = inputStream.read(bytes);
                if (len == -1) {
                    break;
                }
                // 写义目标文件
                outputStream.write(bytes);
                // 强制刷新缓冲区
                outputStream.flush();
            }
            System.out.println("复制成功");

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

由于InputStream和OutputStream实现了Closeable类,所以可以在try()中完成声明,当代码退出try的代码块时,会自动调用close()方法。

继续加油~

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年12月21日
下一篇 2023年12月21日

相关推荐