File IO(NIO.2):讀、寫並創建文件

簡介

本頁討論讀,寫,創建和打開文件的細節。有各種各樣的文件I / O方法可供選擇。爲了幫助理解API,下圖以複雜性排列文件I / O方法


在圖的最左側是實用程序方法readAllBytes,readAllLines和write方法,爲簡單的常見情況設計。右邊是用於迭代流或文本行的方法,例如newBufferedReader,newBufferedWriter,然後是newInputStream和newOutputStream。這些方法可以與java.io包兼容。右邊是處理ByteChannels,SeekableByteChannels和ByteBuffers的方法,比如newByteChannel方法。最後,對於需要文件鎖定或內存映射I / O的高級應用程序,使用FileChannel的方法是最右邊的。

注意:創建新文件的方法可以爲文件指定一組可選的初始屬性。例如,在支持POSIX標準集(如UNIX)的文件系統上,您可以在創建文件時指定文件所有者,組所有者或文件權限。“管理元數據”頁面介紹文件屬性,以及如何訪問和設置它們

操作參數

本節中的幾個方法使用可選的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);

對於Text文件的緩衝IO方法

java.nio.file包支持通道I / O,它可以將數據移入到一個緩衝區中,繞過一些IO的瓶頸

使用緩衝流讀取文件: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);
}

使用IO流創建並輸出一個文件:您可以使用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);
    }
  }
}

用於通道和字節流的方法

使用通道流讀寫文件:當流I / O一次讀取一個字符時,通道I / O一次讀取一個緩衝區。 ByteChannel接口提供基本的讀寫功能。SeekableByteChannel是一個ByteChannel,它具有維持通道中位置並改變該位置的能力。 SeekableByteChannel還支持截斷與通道關聯的文件,並查詢文件的大小。

移動到文件中的不同點,然後從該位置讀取或寫入到該位置的能力使得文件的隨機訪問成爲可能

通道I / O讀取和寫入有兩種方法:newByteChannel(Path, OpenOption...)  和 newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...)

注意:newByteChannel方法返回一個SeekableByteChannel的實例。使用默認文件系統,您可以將此可尋找字節通道轉換爲FileChannel,以提供對更高級功能的訪問,從而將文件的一個區域直接映射到內存中,以便更快的訪問,鎖定文件的一個區域,以便其他進程無法訪問該文件,或從絕對位置讀取和寫入字節,而不影響通道的當前位置。

兩個newByteChannel方法都可以指定一個OpenOption選項列表。除了另外一個選項,還支持newOutputStream方法使用的相同的打開選項: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方法創建新文件,如使用流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
發佈了306 篇原創文章 · 獲贊 230 · 訪問量 57萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章