文档

Java™教程
隐藏目录
读取、写入和创建文件
路径: Java基础类
课程: 基本I/O
章节: 文件I/O (使用NIO.2)

读取、写入和创建文件

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

从最简单(左侧)到最复杂(右侧)排列的文件I/O方法的线条图示。

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

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


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

本页面包含以下主题:


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) {

    // 将字符串转换为字节数组。
    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每次读取一个字符,而通道I/O每次读取一个缓冲区。接口ByteChannel提供了基本的readwrite功能。一个SeekableByteChannel是一个具有在通道中保持位置并更改该位置的能力的ByteChannelSeekableByteChannel还支持截断与通道关联的文件和查询文件的大小。

可以移动到文件中的不同位置,然后从该位置读取或写入,从而实现对文件的随机访问。有关更多信息,请参阅随机访问文件

有两种方法用于读写通道I/O。


注意: newByteChannel方法返回一个SeekableByteChannel实例。在默认文件系统中,您可以将此可寻址字节通道强制转换为FileChannel,以获得更高级的功能,例如将文件的某个区域直接映射到内存中以加快访问速度、锁定文件的某个区域以防止其他进程访问,或者在不影响通道当前位置的情况下读取和写入字节。

newByteChannel方法都支持指定一个OpenOption选项列表。除了支持newOutputStream方法使用的相同的打开选项外,还支持一个额外的选项:READ是必需的,因为SeekableByteChannel同时支持读取和写入。

指定READ打开通道以进行读取。指定WRITEAPPEND打开通道以进行写入。如果没有指定这些选项中的任何一个,那么通道将以读取模式打开。

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

public static void readFile(Path path) throws IOException {

    // Files.newByteChannel() 默认使用 StandardOpenOption.READ
    try (SeekableByteChannel sbc = Files.newByteChannel(path)) {
        final int BUFFER_CAPACITY = 10;
        ByteBuffer buf = ByteBuffer.allocate(BUFFER_CAPACITY);

        // 使用此平台的适当编码读取字节。如果
        // 跳过这一步,您可能会看到外来的或难以辨认的
        // 字符。
        String encoding = System.getProperty("file.encoding");
        while (sbc.read(buf) > 0) {
            buf.flip();
            System.out.print(Charset.forName(encoding).decode(buf));
            buf.clear();
        }
    }    
}

以下示例适用于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) {
  
    // 创建追加到文件的选项集合。
    Set<OpenOption> options = new HashSet<OpenOption>();
    options.add(APPEND);
    options.add(CREATE);

    // 创建自定义权限属性。
    Set<PosixFilePermission> perms =
      PosixFilePermissions.fromString("rw-r-----");
    FileAttribute<Set<PosixFilePermission>> attr =
      PosixFilePermissions.asFileAttribute(perms);

    // 将字符串转换为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 {
    // 使用默认权限创建空文件
    Files.createFile(file);
} catch (FileAlreadyExistsException x) {
    System.err.format("名为%s的文件已存在%n", file);
} catch (IOException x) {
    // 其他类型的失败,如权限等。
    System.err.format("createFile错误:%s%n", x);
}

POSIX文件权限中有一个使用createFile(Path, FileAttribute<?>)创建带有预设权限的文件的示例。

您也可以使用newOutputStream方法创建一个新文件,详见使用流I/O创建和写入文件。如果打开一个新的输出流并立即关闭它,将创建一个空文件。

创建临时文件

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

第一个方法允许代码为临时文件指定一个目录,而第二个方法在默认临时文件目录中创建一个新文件。两种方法都允许您为文件名指定后缀,而第一个方法还允许您指定前缀。以下代码片段给出了第二种方法的示例:

try {
    Path tempFile = Files.createTempFile(null, ".myapp");
    System.out.format("临时文件已创建:%s%n", tempFile)
;
} catch (IOException x) {
    System.err.format("IOException:%s%n", x);
}

运行该文件的结果可能类似于以下内容:

临时文件已创建:/tmp/509668702974537184.myapp

临时文件名的具体格式取决于操作系统。


上一页: 管理元数据(文件和文件存储属性)
下一页: 随机访问文件