# 阅读，编写和创建文件

> 原文： [https://docs.oracle.com/javase/tutorial/essential/io/file.html](https://docs.oracle.com/javase/tutorial/essential/io/file.html)

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

<center>![Line drawing with file I/O methods arranged from least complex (on the left) to most complex (on the right).](img/397932f8b7e5673f794dfe684c590872.jpg)

<center>File I/O Methods Arranged from Less Complex to More Complex</center>

</center>

图的最左侧是实用方法`readAllBytes`，`readAllLines`和`write`方法，专为简单的常见情况而设计。在这些的右边是用于迭代一行或多行文本的方法，例如`newBufferedReader`，`newBufferedWriter`，然后是`newInputStream`和`newOutputStream`。这些方法可与`java.io`包互操作。在这些方法的右边是处理`ByteChannels`，`SeekableByteChannels`和`ByteBuffers`的方法，例如`newByteChannel`方法。最后，在最右边的是使用`FileChannel`的方法，用于需要文件锁定或内存映射 I / O 的高级应用程序。

* * *

**Note:** The methods for creating a new file enable you to specify an optional set of initial attributes for the file. For example, on a file system that supports the POSIX set of standards (such as UNIX), you can specify a file owner, group owner, or file permissions at the time the file is created. The [Managing Metadata](fileAttr.html) page explains file attributes, and how to access and set them.

* * *

此页面包含以下主题：

*   [`OpenOptions`参数](#openOptions)
*   [小文件的常用方法](#common)
*   [文本文件的缓冲 I / O 方法](#textfiles)
*   [无缓冲流的方法和可与`java.io` API 互操作](#streams)
*   [频道和`ByteBuffers`](#channels) 的方法
*   [创建常规和临时文件的方法](#creating)

* * *

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

支持以下`StandardOpenOptions`枚举：

*   `WRITE` - 打开文件以进行写访问。
*   `APPEND` - 将新数据追加到文件末尾。此选项与`WRITE`或`CREATE`选项一起使用。
*   `TRUNCATE_EXISTING` - 将文件截断为零字节。此选项与`WRITE`选项一起使用。
*   `CREATE_NEW` - 如果文件已存在，则创建一个新文件并抛出异常。
*   `CREATE` - 打开文件（如果存在）或创建新文件（如果不存在）。
*   `DELETE_ON_CLOSE` - 关闭流时删除文件。此选项对临时文件很有用。
*   `SPARSE` - 提示新创建的文件将是稀疏的。此高级选项在某些文件系统（例如 NTFS）上受到尊重，其中具有数据“间隙”的大文件可以以更有效的方式存储，其中这些空间隙不占用磁盘空间。
*   `SYNC` - 使文件（内容和元数据）与底层存储设备保持同步。
*   `DSYNC` - 使文件内容与底层存储设备保持同步。

* * *

如果你有一个小文件，并且想要一次读取它的全部内容，你可以使用 [`readAllBytes(Path)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#readAllBytes-java.nio.file.Path-) 或 [`readAllLines(Path, Charset)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#readAllLines-java.nio.file.Path-java.nio.charset.Charset-) 方法。这些方法可以为您完成大部分工作，例如打开和关闭流，但不用于处理大型文件。以下代码显示了如何使用`readAllBytes`方法：

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

```

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

*   [`write(Path, byte[], OpenOption...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#write-java.nio.file.Path-byte:A-java.nio.file.OpenOption...-)
*   [`write(Path, Iterable&lt; extends CharSequence&gt;, Charset, OpenOption...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#write-java.nio.file.Path-java.lang.Iterable-java.nio.charset.Charset-java.nio.file.OpenOption...-)

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

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

```

* * *

`java.nio.file`软件包支持通道 I / O，它可以移动缓冲区中的数据，绕过一些可能会阻塞流 I / O 的层。

[`newBufferedReader(Path, Charset)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newBufferedReader-java.nio.file.Path-java.nio.charset.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...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newBufferedWriter-java.nio.file.Path-java.nio.charset.Charset-java.nio.file.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);
}

```

* * *

要打开文件进行读取，可以使用 [`newInputStream(Path, OpenOption...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newInputStream-java.nio.file.Path-java.nio.file.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...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newOutputStream-java.nio.file.Path-java.nio.file.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);
    }
  }
}

```

* * *

##  的方法

当流 I / O 一次读取一个字符时，通道 I / O 一次读取一个缓冲区。 [`ByteChannel`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/ByteChannel.html) 接口提供基本的`read`和`write`功能。 [`SeekableByteChannel`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/SeekableByteChannel.html) 是`ByteChannel`，能够维持通道中的位置并改变该位置。 `SeekableByteChannel`还支持截断与通道关联的文件并查询文件的大小。

移动到文件中的不同点然后从该位置读取或写入的能力使得可以随机访问文件。有关详细信息，请参阅[随机存取文件](rafs.html)。

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

*   [`newByteChannel(Path, OpenOption...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newByteChannel-java.nio.file.Path-java.nio.file.OpenOption...-)
*   [`newByteChannel(Path, Set&lt;? extends OpenOption&gt;, FileAttribute&lt;?&gt;...)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#newByteChannel-java.nio.file.Path-java.util.Set-java.nio.file.attribute.FileAttribute...-)

* * *

**Note:** The `newByteChannel` methods return an instance of a `SeekableByteChannel`. With a default file system, you can cast this seekable byte channel to a [`FileChannel`](https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html) providing access to more advanced features such mapping a region of the file directly into memory for faster access, locking a region of the file so other processes cannot access it, or reading and writing bytes from an absolute position without affecting the channel's current position.

* * *

两种`newByteChannel`方法都可以指定`OpenOption`选项列表。除了另外一个选项外，还支持`newOutputStream`方法使用的相同[打开选项](#openOptions)：`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&lt;?&gt;)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#createFile-java.nio.file.Path-java.nio.file.attribute.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 文件权限](fileAttr.html#posix)有一个使用`createFile(Path, FileAttribute&lt;?&gt;)`创建具有预设权限的文件的示例。

您也可以使用`newOutputStream`方法创建新文件，如[使用流 I / O 创建和写入文件](file.html#createStream)中所述。如果打开新输出流并立即关闭它，则会创建一个空文件。

您可以使用以下`createTempFile`方法之一创建临时文件：

*   [`createTempFile(Path, String, String, FileAttribute&lt;?&gt;)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#createTempFile-java.nio.file.Path-java.lang.String-java.lang.String-java.nio.file.attribute.FileAttribute...-)
*   [`createTempFile(String, String, FileAttribute&lt;?&gt;)`](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#createTempFile-java.lang.String-java.lang.String-java.nio.file.attribute.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

```

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