文件是 Java 应用程序中常见的数据源或目的地。因此,本文将简要介绍如何在 Java 中处理文件。我们无意在此详细解释每种技术,而是希望为你提供足够的知识来决定文件访问方法。
Java IO API 包含以下与在 Java 中处理文件有关的类:
File:表示文件或目录的抽象路径名。
RandomAccessFile:支持随机读写文件的类。
FileInputStream:用于从文件读取字节的流。
FileReader:用于从文件读取字符的流。
FileOutputStream:用于向文件写入字节的流。
FileWriter:用于向文件写入字符的流。
下文将简要介绍这些类。
API 文档地址:https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html
如果需要读取文件内容,可以使用 FileInputStream 或 FileReader 类。具体选择哪一个,取决于你想读取的是二进制数据还是文本数据。这两个类可以让你从文件中一次读取一个字节(byte)或字符(char),或者将字节/字符读入到一个字节/字符数组。
注意,这两个类读取文件必须从文件开头,逐一读取到文件末尾,不能随机读取。如下图:
其实,你可以将文件内容想象成一个大的字节或字符数组,我们读取文件时只能从下标为 0 的位置开始遍历。
示例:
package com.hxstrive.java_io.inputstream; import java.io.FileInputStream; /** * 读取文件示例 * @author hxstrive.com */ public class FileInputStreamDemo1 { public static void main(String[] args) { try(FileInputStream fileInputStream = new FileInputStream("D:\\input.txt")) { byte[] bytes = new byte[fileInputStream.available()]; int len = fileInputStream.read(bytes); System.out.println(len); // 13 System.out.println(new String(bytes)); // Hello Java IO } catch (Exception e) { e.printStackTrace(); } } }
如果需要在文件中跳转(即跳过 N 个字节或字符),只从这里或那里读取部分内容,可以使用 RandomAccessFile(随机读取文件类)。
如果需要将数据写入文件,可以使用 FileOutputStream 或 FileWriter 类,具体选择哪一个类取决于需要写入的是二进制数据还是字符。你可以每次写入一个字节(byte)或字符(char),也可以写入字节和字符数组。
注意:
(1)数据会按照写入的顺序依次存储在文件中。
(2)仅支持从文件开头一次写入到文件末尾,不能随机将字节或字符写入到文件中指定的位置。
示例:
package com.hxstrive.java_io.outputstream; import java.io.FileInputStream; import java.io.FileOutputStream; /** * 写文件示例 * @author hxstrive.com */ public class FileOutputStreamDemo { public static void main(String[] args) { try(FileOutputStream output = new FileOutputStream("D:\\output.txt"); FileInputStream input = new FileInputStream("D:\\output.txt")) { // 写出数据 output.write("Hello World!".getBytes()); output.flush(); // 读取数据 byte[] buffer = new byte[1024]; int len = input.read(buffer); System.out.println(new String(buffer, 0, len)); // Hello World! } catch (Exception e) { e.printStackTrace(); } } }
如果需要跳过文件并在不同位置写入数据,例如追加到文件末尾,可以使用 RandomAccessFile。
如前所述,你可以通过 RandomAccessFile 类使用 Java IO 随机访问文件。
随机存取并不意味着可以从真正随机的地方读取或写入。它只是意味着你可以跳过文件,以任何你想要的方式同时读取或写入文件。这就是“随机”的含义 —— 下一个字节的读取并不取决于前一个字节的读取,不强制执行特定的访问顺序。你可以 “随机”—— 任意地访问文件中的字节。这样,就可以覆盖现有文件的某些部分、向文件添加内容、删除文件,当然也可以从需要读取文件的地方读取文件。
如下图,从文件两个位置读取内容,先从文件末尾读取“位置1”对应的内容,然后再次从文件“位置2”读取对应的内容:
示例:
package com.hxstrive.java_io.other; import java.io.IOException; import java.io.RandomAccessFile; /** * 随机访问文件 * @author hxstrive */ public class RandomAccessFileDemo { public static void main(String[] args) { String fileName = "D:\\random.txt"; try (RandomAccessFile file = new RandomAccessFile(fileName, "rw")) { // 写入数据 file.writeUTF("Hello, World!"); // 将文件指针移到文件开头 file.seek(0); // 读取数据 System.out.println("读取的数据: " + file.readUTF()); // 读取的数据: Hello, World! // 修改数据 file.seek(7); // 将文件指针移到第 8 个字符的位置 file.writeUTF("Java"); // 将文件指针移到文件开头 file.seek(0); // 再次读取数据 System.out.println("修改后的数据: " + file.readUTF()); // 修改后的数据: Hello Javad! } catch (IOException e) { e.printStackTrace(); } } }
有时,你可能需要访问文件的相关信息而不是其内容。例如,你需要知道文件大小或文件属性。目录的情况也是如此。例如,你可能想获得一个给定目录中所有文件的列表。文件和目录信息都可以通过文件类获取。
示例:
package com.hxstrive.java_io.file; import java.io.File; /** * 获取文件基本信息 * @author hxstrive.com */ public class FileDemo { public static void main(String[] args) { File file = new File("D:\\input.txt"); System.out.println("文件名:" + file.getName()); // 文件名:input.txt System.out.println("文件路径:" + file.getPath()); // 文件路径:D:\input.txt System.out.println("存在?" + file.exists()); // 存在?true System.out.println("是文件?" + file.isFile()); // 是文件?true System.out.println("是目录?" + file.isDirectory()); // 是目录?false System.out.println("文件长度:" + file.length()); // 文件长度:13 System.out.println("最后修改日期:" + file.lastModified()); // 最后修改日期:1735194454080 System.out.println("能读?" + file.canRead()); // 能读?true System.out.println("能写?" + file.canWrite()); // 能写?true System.out.println("能执行?" + file.canExecute()); // 能执行?true } }
递归访问一个目录下面所有到文件,例如:
package com.hxstrive.java_io.file; import java.io.File; import java.util.Objects; /** * 递归遍历目录 * @author hxstrive.com */ public class FileDemo2 { public static void main(String[] args) { File file = new File("D:\\demo"); if(!file.isDirectory()) { // 不是目录 System.out.println(file.getAbsoluteFile()); return; } // 是目录 File[] files = file.listFiles(); if(Objects.nonNull(files)) { show(file); } } /** * 递归显示目录内容 * @param file 文件/目录 */ private static void show(File file) { if(file.isDirectory()) { File[] files = file.listFiles(); if(Objects.nonNull(files)) { for(File f : files) { if(f.isDirectory()) { System.out.println(f.getAbsoluteFile()); show(f); } else { System.out.println(f.getAbsoluteFile()); } } } } else { System.out.println(file.getAbsoluteFile()); } } }
使用 tree D:/demo /F /A 命令查看 demo 目录到结构,如下图:
运行结果如下:
D:\demo\c++ D:\demo\c++\Hello.c D:\demo\Hello.java D:\demo\java D:\demo\java\network D:\demo\java\network\Echo.java D:\demo\java\Test.java