文档

Java™ 教程-Java Tutorials 中文版
读取、写入和创建文件
Trail: Essential Classes
Lesson: Basic I/O
Section: File I/O (Featuring NIO.2)

读取、写入和创建文件

本页讨论读取、写入、创建和打开文件的详细信息。有多种文件 I/O 方法可供选择。为了帮助理解 API,下图按复杂性排列文件 I/O 方法。

使用从最不复杂(在左侧)到最复杂(在右侧)排列的文件 I/O 方法的线条图。

文件 I/O 方法从较简单到最复杂排列

图的最左侧是实用方法 readAllBytesreadAllLineswrite 方法,专为简单的常见情况而设计。在这些方法的右侧是用于迭代一行或多行文本的方法,例如 newBufferedReadernewBufferedWriter,然后 newInputStreamnewOutputStream。这些方法可与 java.io 包互操作。在这些方法的右边是处理 ByteChannelsSeekableByteChannelsByteBuffers 的方法,例如 newByteChannel 方法。最后,最右边是使用 FileChannel 的方法,用于需要文件锁定或内存映射 I/O 的高级应用程序。


注意: 创建新文件的方法使你可以为文件指定一组可选的初始属性。例如,在支持 POSIX 标准集(例如 UNIX)的文件系统上,你可以在创建文件时指定文件所有者,组所有者或文件权限。Managing Metadata 页面解释了文件属性以及如何访问和设置它们。

此页面包含以下主题:


OpenOptions 参数

本节中的几个方法采用可选的 OpenOptions 参数。此参数是可选的,API 会告诉你在未指定此参数时该方法的默认行为。

支持以下 StandardOpenOptions 枚举:


小文件的常用方法

从文件中读取所有字节或行

如果你有一个小文件并且想要一次读取它的全部内容,你可以使用 readAllBytes(Path)readAllLines(Path, Charset) 方法。这些方法可以为你完成大部分工作,例如打开和关闭流,但不用于处理大型文件。以下代码显示了如何使用 readAllBytes 方法:

Path file = ...;
byte[] fileArray;
fileArray = Files.readAllBytes(file);

将所有字节或行写入文件

你可以使用其中一种写入方法将字节或行写入文件。

以下代码段显示了如何使用 write 方法。

Path file = ...;
byte[] buf = ...;
Files.write(file, buf);


文本文件的缓冲 I/O 方法

java.nio.file 包支持通道 I/O,它可以在缓冲区中移动数据,绕过一些可能会阻塞流 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);
}

使用缓冲流 I/O 写入文件

你可以使用 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 互操作的方法

使用流 I/O 读取文件

要打开文件进行读取,可以使用 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);
}

使用流 I/O 创建和写入文件

你可以使用 newOutputStream(Path, OpenOption...) 方法创建文件,附加到文件或写入文件。此方法打开或创建用于写入字节的文件,并返回无缓冲的输出流。

该方法采用可选的 OpenOption 参数。如果未指定打开选项,并且该文件不存在,则会创建一个新文件。如果文件存在,则会被截断。此选项等效于使用 CREATETRUNCATE_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);
    }
  }
}

用于 Channels 和 ByteBuffers 的方法

使用通道 I/O 读取和写入文件

当流 I/O 一次读取一个字符时,通道 I/O 一次读取一个缓冲区。ByteChannel 接口提供基本的 readwrite 功能。SeekableByteChannel 是一个 ByteChannel,它能够维持通道中的位置并更改该位置。SeekableByteChannel 还支持截断与频道关联的文件并查询文件的大小。

移动到文件中的不同点然后从该位置读取或写入的能力使得可以随机访问文件。有关详细信息,请参阅 Random Access Files

读取和写入通道 I/O 有两种方法。


注意: newByteChannel 方法返回 SeekableByteChannel 的实例。使用默认文件系统,你可以将此可搜索字节通道转换为 FileChannel,从而提供对更高级功能的访问,例如将文件区域直接映射到内存中以便更快地访问,锁定文件的一个区域,以便其他进程无法访问它,或者从绝对位置读取和写入字节而不影响通道的当前位置。

两种 newByteChannel 方法都允许你指定 OpenOption 选项的列表。支持使用与 newOutputStream 方法相同的 open options:除此外还多一个选项,READ 是必需的,因为 SeekableByteChannel 支持读取和写入。

指定 READ 将打开要读取的通道。指定 WRITEAPPEND 将打开要写入的通道。如果未指定这些选项,则打开通道进行读取。

以下代码段读取文件并将其打印到标准输出:

// 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 方法之一创建临时文件:

第一种方法允许代码指定临时文件的目录,第二种方法在默认临时文件目录中创建新文件。这两种方法都允许你为文件名指定后缀,第一种方法允许你指定前缀。(译注:根据文档和测试,两者都支持前缀、后缀,只是第一种支持指定目录)以下代码段给出了第二种方法的示例:

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

临时文件名的特定格式是特定于平台的。


Previous page: Managing Metadata (File and File Store Attributes)
Next page: Random Access Files