Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
本页讨论读取、写入、创建和打开文件的详细信息。有多种文件 I/O 方法可供选择。为了帮助理解 API,下图按复杂性排列文件 I/O 方法。
图的最左侧是实用方法 readAllBytes
,readAllLines
和 write
方法,专为简单的常见情况而设计。在这些方法的右侧是用于迭代一行或多行文本的方法,例如 newBufferedReader
,newBufferedWriter
,然后 newInputStream
和 newOutputStream
。这些方法可与 java.io
包互操作。在这些方法的右边是处理 ByteChannels
,SeekableByteChannels
和 ByteBuffers
的方法,例如 newByteChannel
方法。最后,最右边是使用 FileChannel
的方法,用于需要文件锁定或内存映射 I/O 的高级应用程序。
此页面包含以下主题:
OpenOptions
参数java.io
API 互操作的方法ByteBuffers
的方法OpenOptions
参数本节中的几个方法采用可选的 OpenOptions
参数。此参数是可选的,API 会告诉你在未指定此参数时该方法的默认行为。
支持以下 StandardOpenOptions
枚举:
WRITE
- 打开文件以进行写访问。APPEND
- 将新数据附加到文件末尾。此选项与 WRITE
或 CREATE
选项一起使用。TRUNCATE_EXISTING
- 将文件截断为零字节。此选项与 WRITE
选项一起使用。CREATE_NEW
- 创建一个新文件,如果文件已存在,则抛出异常。CREATE
- 打开文件(如果存在)或创建新文件(如果不存在)。DELETE_ON_CLOSE
- 关闭流时删除文件。此选项对临时文件很有用。SPARSE
- 提示新创建的文件将是稀疏的。此高级选项在某些文件系统(例如 NTFS)上受到尊重,其中具有数据“间隙”的大文件可以以更有效的方式存储,其中这些空间隙不占用磁盘空间。SYNC
- 使文件(内容和元数据)与底层存储设备保持同步。DSYNC
- 使文件内容与底层存储设备保持同步。如果你有一个小文件并且想要一次读取它的全部内容,你可以使用 readAllBytes(Path)
或 readAllLines(Path, Charset)
方法。这些方法可以为你完成大部分工作,例如打开和关闭流,但不用于处理大型文件。以下代码显示了如何使用 readAllBytes
方法:
Path file = ...; byte[] fileArray; fileArray = Files.readAllBytes(file);
你可以使用其中一种写入方法将字节或行写入文件。
write(Path, byte[], OpenOption...)
write(Path, Iterable< extends CharSequence>, Charset, OpenOption...)
以下代码段显示了如何使用 write
方法。
Path file = ...; byte[] buf = ...; Files.write(file, buf);
java.nio.file
包支持通道 I/O,它可以在缓冲区中移动数据,绕过一些可能会阻塞流 I/O 的层。
newBufferedReader(Path, Charset)
方法打开一个文件进行读取,返回一个 BufferedReader
,可用于以有效的方式从文件中读取文本。
以下代码段显示了如何使用 newBufferedReader
方法从文件中读取。该文件以“US-ASCII”编码。
Charset charset = Charset.forName("US-ASCII"); try (BufferedReader reader = Files.newBufferedReader(file, charset)) { String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException x) { System.err.format("IOException: %s%n", x); }
你可以使用 newBufferedWriter(Path, Charset, OpenOption...)
方法使用 BufferedWriter
写入文件。
以下代码段显示了如何使用此方法创建以“US-ASCII”编码的文件:
Charset charset = Charset.forName("US-ASCII"); String s = ...; try (BufferedWriter writer = Files.newBufferedWriter(file, charset)) { writer.write(s, 0, s.length()); } catch (IOException x) { System.err.format("IOException: %s%n", x); }
java.io
API 互操作的方法要打开文件进行读取,可以使用 newInputStream(Path, OpenOption...)
方法。此方法返回一个无缓冲的输入流,用于从文件中读取字节。
Path file = ...; try (InputStream in = Files.newInputStream(file); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException x) { System.err.println(x); }
你可以使用 newOutputStream(Path, OpenOption...)
方法创建文件,附加到文件或写入文件。此方法打开或创建用于写入字节的文件,并返回无缓冲的输出流。
该方法采用可选的 OpenOption
参数。如果未指定打开选项,并且该文件不存在,则会创建一个新文件。如果文件存在,则会被截断。此选项等效于使用 CREATE
和 TRUNCATE_EXISTING
选项调用方法。
以下示例打开一个日志文件。如果该文件不存在,则创建该文件。如果该文件存在,则打开该文件以进行追加。
import static java.nio.file.StandardOpenOption.*; import java.nio.file.*; import java.io.*; public class LogFileTest { public static void main(String[] args) { // Convert the string to a // byte array. String s = "Hello World! "; byte data[] = s.getBytes(); Path p = Paths.get("./logfile.txt"); try (OutputStream out = new BufferedOutputStream( Files.newOutputStream(p, CREATE, APPEND))) { out.write(data, 0, data.length); } catch (IOException x) { System.err.println(x); } } }
ByteBuffers
的方法当流 I/O 一次读取一个字符时,通道 I/O 一次读取一个缓冲区。ByteChannel
接口提供基本的 read
和 write
功能。SeekableByteChannel
是一个 ByteChannel
,它能够维持通道中的位置并更改该位置。SeekableByteChannel
还支持截断与频道关联的文件并查询文件的大小。
移动到文件中的不同点然后从该位置读取或写入的能力使得可以随机访问文件。有关详细信息,请参阅 Random Access Files。
读取和写入通道 I/O 有两种方法。
newByteChannel(Path, OpenOption...)
newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...)
newByteChannel
方法返回 SeekableByteChannel
的实例。使用默认文件系统,你可以将此可搜索字节通道转换为 FileChannel
,从而提供对更高级功能的访问,例如将文件区域直接映射到内存中以便更快地访问,锁定文件的一个区域,以便其他进程无法访问它,或者从绝对位置读取和写入字节而不影响通道的当前位置。
两种 newByteChannel
方法都允许你指定 OpenOption
选项的列表。支持使用与 newOutputStream
方法相同的 open options:除此外还多一个选项,READ
是必需的,因为 SeekableByteChannel
支持读取和写入。
指定 READ
将打开要读取的通道。指定 WRITE
或 APPEND
将打开要写入的通道。如果未指定这些选项,则打开通道进行读取。
以下代码段读取文件并将其打印到标准输出:
// Defaults to READ try (SeekableByteChannel sbc = Files.newByteChannel(file)) { ByteBuffer buf = ByteBuffer.allocate(10); // Read the bytes with the proper encoding for this platform. If // you skip this step, you might see something that looks like // Chinese characters when you expect Latin-style characters. String encoding = System.getProperty("file.encoding"); while (sbc.read(buf) > 0) { buf.rewind(); System.out.print(Charset.forName(encoding).decode(buf)); buf.flip(); } } catch (IOException x) { System.out.println("caught exception: " + x);
以下示例是为 UNIX 和其他 POSIX 文件系统编写的,它创建了一个具有一组特定文件权限的日志文件。此代码创建日志文件或附加到日志文件(如果已存在)。创建日志文件时,对所有者具有读/写权限,对组具有只读权限。
import static java.nio.file.StandardOpenOption.*; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.io.*; import java.util.*; public class LogFilePermissionsTest { public static void main(String[] args) { // Create the set of options for appending to the file. Set<OpenOption> options = new HashSet<OpenOption>(); options.add(APPEND); options.add(CREATE); // Create the custom permissions attribute. Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r-----"); FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); // Convert the string to a ByteBuffer. String s = "Hello World! "; byte data[] = s.getBytes(); ByteBuffer bb = ByteBuffer.wrap(data); Path file = Paths.get("./permissions.log"); try (SeekableByteChannel sbc = Files.newByteChannel(file, options, attr)) { sbc.write(bb); } catch (IOException x) { System.out.println("Exception thrown: " + x); } } }
你可以使用 createFile(Path, FileAttribute<?>)
方法创建具有初始属性集的空文件。例如,如果在创建时希望文件具有特定的文件权限集,请使用 createFile
方法执行此操作。如果未指定任何属性,则使用默认属性创建文件。如果该文件已存在,createFile
将引发异常。
在单个原子操作中,createFile
方法检查文件是否存在,并使用指定的属性创建该文件,这使得该过程对恶意代码更安全。
以下代码段创建一个具有默认属性的文件:
Path file = ...; try { // Create the empty file with default permissions, etc. Files.createFile(file); } catch (FileAlreadyExistsException x) { System.err.format("file named %s" + " already exists%n", file); } catch (IOException x) { // Some other sort of failure, such as permissions. System.err.format("createFile error: %s%n", x); }
POSIX 文件权限有一个示例,它使用 createFile(Path, FileAttribute<?>)
创建具有预设权限的文件。
你还可以使用 newOutputStream
方法创建新文件,如 Creating and Writing a File using Stream I/O 中所述。如果打开新输出流并立即关闭它,则会创建一个空文件。
你可以使用以下 createTempFile
方法之一创建临时文件:
createTempFile(Path, String, String, FileAttribute<?>)
createTempFile(String, String, FileAttribute<?>)
第一种方法允许代码指定临时文件的目录,第二种方法在默认临时文件目录中创建新文件。这两种方法都允许你为文件名指定后缀,第一种方法允许你指定前缀。(译注:根据文档和测试,两者都支持前缀、后缀,只是第一种支持指定目录)以下代码段给出了第二种方法的示例:
try { Path tempFile = Files.createTempFile(null, ".myapp"); System.out.format("The temporary file" + " has been created: %s%n", tempFile) ; } catch (IOException x) { System.err.format("IOException: %s%n", x); }
运行此文件的结果如下所示:
The temporary file has been created: /tmp/509668702974537184.myapp
临时文件名的特定格式是特定于平台的。