读取,写入和创建文件

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

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

文件 I/O 方法从不太复杂到更复杂

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

Note:

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

此页面包含以下主题:


OpenOptions 参数

本节中的几种方法采用可选的OpenOptions参数。此参数是可选的,当未指定任何参数时,API 会告诉您该方法的默认行为是什么。

支持以下StandardOpenOptions枚举:

  • WRITE –打开文件以进行写访问。

  • APPEND –将新数据追加到文件末尾。此选项与WRITECREATE选项一起使用。

  • 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 file = ...;
byte[] buf = ...;
Files.write(file, buf);

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

java.nio.file包支持通道 I/O,该通道将数据移动到缓冲区中,从而绕过了一些可能会阻塞流 I/O 的层。

使用缓冲流 I/O 读取文件

newBufferedReader(Path, Charset)方法打开一个文件进行读取,并返回一个BufferedReader,该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...)方法。此方法返回一个无缓冲的 Importing 流,用于从文件中读取字节。

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);
    }
  }
}

通道和字节缓冲区的方法

使用通道 I/O 读写文件

流 I/O 一次读取一个字符,而通道 I/O 一次读取一个缓冲区。 ByteChannelinterface提供基本的readwrite功能。 SeekableByteChannelByteChannel,它具有维护通道中的位置并更改该位置的能力。 SeekableByteChannel还支持截断与通道关联的文件并查询文件的大小。

能够移动到文件中的不同点,然后从该位置读取或写入该位置的功能使对文件的随机访问成为可能。有关更多信息,请参见随机存取 Files

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

Note:

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

两种newByteChannel方法都使您可以指定OpenOption选项的列表。除了一个选项之外,还支持newOutputStream方法使用的相同open optionsREAD是必需的,因为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);
    }
  }
}

创建常规文件和临时文件的方法

Creating Files

您可以使用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<?>)创建具有预设权限的文件。

您还可以使用使用流 I/O 创建和写入文件中所述的newOutputStream方法来创建新文件。如果打开一个新的输出流并立即将其关闭,则会创建一个空文件。

创建临时文件

您可以使用以下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

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