天天看點

Java SE 7 新特性之檔案操作(6) - 建立和讀寫檔案

轉自 開發者的天空

本文中我們來讨論在NIO2 中怎樣建立檔案 、讀取檔案和寫檔案。NIO2提供了多種建立 檔案的方法,使得我們在建立檔案的時候就可以指定檔案的某些初始屬性。例如在支援POSIX的檔案系統上指定檔案的所有者,通路權限等。關于檔案的屬性, 請看上一篇文章Java SE 7新特性之檔案操作 (5) - 管理中繼資料

建立檔案

可以調用createFile(FileAttribute<?>)方法建立一個空檔案。該方法的參數就是檔案的初始屬性。下面的例子是怎樣 在建立檔案的時候賦予該檔案某些權限的屬性:

Path sourceFile = ...;

Path newFile = ...;

PosixFileAttributes attrs = Attributes.readPosixFileAttributes(sourceFile);

FileAttribute<Set<PosixFilePermission>> attr =

          PosixFilePermissions.asFileAttribute(attrs.permissions());

try {

    file.createFile(attr);

} catch (IOException x) {

    //unable to create the file

}

如 果在調用該方法的時候沒有傳入任何參數,那麼建立的檔案将具有預設的檔案屬性。下面的代碼建立了一個具有預設檔案屬性的檔案:

Path file = ...;

try {

    file.createFile();   //Create the empty file with default permissions, etc.

} catch (FileAlreadyExists 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);

}

如 果要建立的檔案已經存在,該方法會抛出異常。

注意在調用createFile方法時,檢查檔案是否存在和建立具有特定的屬性的檔案是在同一個原子操作中。

還可以使用newOutputSteam方法來建立檔案,在本文的後面我們會講到怎樣使用newOutputStream方法來建立檔案。

通過Stream I/O讀檔案

我們可以通過newInputStream(OpenOption...)方法打開和讀取檔案。這個方法傳回一個unbuffered輸入流(input stream),我們可以用它來從檔案中讀取位元組内容。

Path file = ...;

InputStream in = null;

try {

    in = file.newInputStream();

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

} finally {

    if (in != null) in.close();

}

注 意該方法接受可變個數的參數,參數類型為OpenOption,指定了檔案怎樣打開。如果不傳入參數,則使用預設的READ方式打開。READ方式是所有 的實作都支援的方式。有一些實作也支援其他的打開方式。

如果傳入的OpenOption或其組合不正确,會抛出異常。如果程式沒有讀權限或I/O錯誤,也會抛出異常。

Creating and Writing a File by Using Stream I/O

使用Stream I/O來建立和寫檔案

我們可以使用newOutputStream方法來建立檔案、擴充檔案或覆寫已有檔案。這個方法為了寫檔案而打開或建立檔案,該方法傳回一個 unbuffered的輸出流(output stream)。newOutputStream方法有兩種形式:

  • newOutputStream(OpenOption...)
  • newOutputStream(Set<? extends OpenOption>, FileAttribute<?>...)

這兩種形式都接受一組OpenOption作為參數,第二種形式還允許指定初始的檔案屬性。這個方法支援的StandardOpenOption有:

  • WRITE –為了寫通路而打開該檔案.
  • APPEND – 将新資料擴充在檔案的末尾。該選項和WRITE或CREATE選項一起使用。
  • TRUNCATE_EXISTING – 将檔案截斷為0位元組長. 該選項和WRITE選項一起使用來覆寫原有檔案。
  • CREATE_NEW – 建立一個新的檔案。如果原來的檔案存在這抛出異常。
  • CREATE – 如果原檔案存在這打開它,否則建立新的檔案。
  • DELETE_ON_CLOSE – 當Stream關閉時删除該檔案。這個選項對臨時檔案比較有用。
  • SPARSE – 表明新建立的檔案是Sparse檔案. 關于Sparse檔案的具體資訊請看http://space.itpub.net/8242091/viewspace-619756 。
  • SYNC – 保持該檔案(包括内容和中繼資料)與底層儲存設備同步。
  • DSYNC – 保持檔案内容與底層儲存設備同步。

如果沒有指定OpenOption,該方法的行為是:如果檔案不存在,則建立新檔案;如果檔案存在,那麼截斷它。也就是說預設的選擇是CREATE和 TRUNCATE_EXISTING選項的組合。

下面的代碼打開一個日志 檔案,如果檔案不存在,則建立一個新檔案。如果檔案 存在,這将新的内容擴充到檔案尾部。

import static java.nio.file.StandardOpenOption.*;

Path logfile = ...;

//Convert the string to a byte array.

String s = ...;

byte data[] = s.getBytes();

OutputStream out = null;

try {

    out = new BufferedOutputStream(logfile.newOutputStream(CREATE, APPEND));

    ...

    out.write(data, 0, data.length);

} catch (IOException x) {

    System.err.println(x);

} finally {

    if (out != null) {

        out.flush();

        out.close();

    }

}

使用Channel I/O來讀寫檔案

Stream I/O每次讀取一個字元,Channel I/O每次讀取一個緩沖塊的資料。ByteChannel接口提供了基本的讀寫功能。SeekableByteChannel擴充了 ByteChannel并提供了維護一個channel中的位置并改變該位置的能力。SeekableByteChannel還支援截斷檔案和查詢檔案大 小的功能。

移動到檔案中不同的位置,從該位置開始讀或寫的能力使我們可以随機通路檔案 。有兩種形式的 newByteChannel方法可以用來讀或寫檔案,這兩種形式和newOutputStream方法一樣。

  • newByteChannel(OpenOption...)
  • newByteChannel(Set<? extends OpenOption>, FileAttribute<?>...)

這兩個方法都允許指定OpenOption,newOutputStream所支援的選擇這裡也支援,而且這裡還支援另外一個選項READ,因為 SeekableByteChannel既支援讀也支援寫。

如果選項是READ,那麼該channel就是為了讀通路打開。如果選項是WRITE或APPEND,則該channel就是為了寫通路打開。如果沒有指 定,該channel預設是為了讀打開。

下面的代碼從檔案中讀取内容并輸出到控制台上:

SeekableByteChannel sbc = null;

try {

    sbc = file.newByteChannel();  //Defaults to READ

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

} finally {

    if (sbc != null) sbc.close();

}

下 面的代碼是為了UNIX或其他支援POSIX的檔案系統編寫的。這段代碼建立一個新的日志檔案或者擴充原有的日志檔案,該日志檔案建立時指定了通路權限 (所有者有讀寫權限,同組使用者隻有讀權限,其他使用者沒有讀權限)。

import static java.nio.file.StandardCopyOption.*;

//Create the set of options for appending to the file.

Set<OpenOptions> 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 ByetBuffer.

String s = ...;

byte data[] = s.getBytes();

ByteBuffer bb = ByteBuffer.wrap(data);

SeekableByteChannel sbc = null;

try {

    sbc = file.newByteChannel(options, attr);

    sbc.write(bb);

} catch (IOException x) {

    System.out.println("exception thrown: " + x);

} finally {

    if (sbc != null) sbc.close();

}