Java檔案內容和讀寫


檔案的內容型別

Files.probeContentType(Path path)方法探測檔案的內容型別。該方法以多用途網際網路郵件擴充套件(MIME)內容型別的值的字串形式返回內容型別。如果無法確定檔案的內容型別,則返回null
以下程式碼顯示如何探測檔案的內容型別。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    Path p = Paths.get("C:\\Java_Dev\\test1.txt");

    try {
      String contentType = Files.probeContentType(p);
      System.out.format("Content type   of  %s  is %s%n", p, contentType);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的程式碼生成以下結果。

Content type   of  C:\Java_Dev\test1.txt  is text/plain

讀取檔案的內容

Files類包含以下方法來讀取檔案的內容作為位元組和文字行:

  • static byte[] readAllBytes(Path path) - reads all bytes from a file.
  • static List readAllLines(Path path) - reads the entire contents of a file lines of text.
  • static List readAllLines(Path path, Charset cs)

Files類可以從Path物件獲取InputStreamBufferedReader物件。newInputStream(Path path,OpenOption ... options)方法返回指定路徑的InputStream物件。它假定檔案的內容是UTF-8字元集。

newBufferedReader(Path path)newBufferedReader(Path path,Charset cs)方法返回一個BufferedReader。我們可以指定字元集。Files類提供了使用其newByteChannel(Path path,OpenOption ... options)方法從Path物件中獲取SeekableByteChannel物件的方法。

OpenOption型別組態正在開啟的檔案。下表列出了OpenOption型別的值及其描述。OpenOptionjava.nio.file包中的一個介面。java.nio.file包中的StandardOpenOption列舉實現了OpenOption介面。

標準開啟選項 描述
APPEND 將寫入的資料附加到現有檔案,如果檔案被開啟寫入。
CREATE 建立一個新檔案,如果它不存在。
CREATE_NEW 如果檔案不存在,則建立一個新檔案。 如果檔案已存在,則操作失敗。
DELETE_ON_CLOSE 關閉流時刪除檔案。在與臨時檔案一起使用時非常有用。
DSYNC 保持檔案的內容與底層儲存同步。
READ 開啟具有讀存取許可權的檔案。
SPARSE 如果它與CREATE_NEW選項一起使用,它對檔案系統提示新檔案應該是稀疏檔案。
SYNC 保持檔案的內容和後設資料與底層儲存同步。
TRUNCATE_EXISTING 如果開啟檔案以進行寫存取,則將現有檔案的長度截斷為零。
WRITE 開啟檔案以進行寫存取。

以下程式碼實現在預設目錄中為test2.txt檔案獲取一個SeekableByteChannel物件。它開啟檔案以進行讀取和寫入存取。它使用CREATE選項,因此如果檔案不存在,則建立該檔案。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;

import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path src = Paths.get("test2.txt");
    SeekableByteChannel sbc = Files.newByteChannel(src, READ, WRITE, CREATE);
  }
}

以下程式碼演示了如何讀取和顯示預設目錄中test1.txt檔案的內容。 如果檔案不存在,程式將顯示一條錯誤訊息。

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class Main {
  public static void main(String[] args) throws Exception{
    Charset cs = Charset.forName("US-ASCII");
    Path source = Paths.get("test1.txt");

    List<String> lines = Files.readAllLines(source, cs);
    for (String line : lines) {
        System.out.println(line);
    }
  }
}

寫入檔案

使用Files類的以下write()方法將內容寫入檔案。

static Path  write(Path path, byte[]  bytes,  OpenOption... options)
static Path  write(Path path, Iterable lines, OpenOption... options)
static Path  write(Path path, Iterable lines, Charset cs, OpenOption... options)

write()方法開啟檔案,將傳遞的內容寫入檔案,並關閉它。如果沒有開啟選項,它將使用CREATETRUNCATE_EXISTINGWRITE選項開啟檔案。
如果正在向檔案寫入文字,它會寫一個平台相關的行分隔符。如果在寫入文字行時未指定字元集,則預設使用UTF-8字元集。
以下程式碼演示了如何使用write()方法將文字行寫入檔案。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> texts = new ArrayList<>();
        texts.add("test line - 1");
        texts.add("test line - 2");
        Path dest = Paths.get("C:\\Java_Dev\\twinkle.txt");
        Charset cs = Charset.forName("US-ASCII");
        try {
            Path p = Files.write(dest, texts, cs, WRITE, CREATE);
            System.out.println("Text was written to " + p.toAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Files.newOutputStream(Path path,OpenOption ... options)返回指定路徑的OutputStream
Files.newBufferedWriter(Path path, Charset cs, OpenOption... options) 方法為指定的路徑返回BufferedWriter

執行上面程式碼,得到以下結果 -

Text was written to C:\Java_Dev\twinkle.txt

隨機存取檔案

SeekableByteChannel物件提供對檔案的隨機存取。使用Files類的newByteChannel()方法為Path獲取一個SeekableByteChannel物件,如下所示:

Path  src = Paths.get("test.txt"); 
SeekableByteChannel seekableChannel  = Files.newByteChannel(src, READ,  WRITE,  CREATE,  TRUNCATE_EXISTING);

使用size()方法以位元組為單位獲取SeekableByteChannel實體的大小。由於資料被截斷或寫入通道,因此更新了大小。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) {
        Path src = Paths.get("test.txt");
        String encoding = System.getProperty("file.encoding");
        Charset cs = Charset.forName(encoding);
        try (SeekableByteChannel seekableChannel = Files.newByteChannel(src, READ, WRITE, CREATE, TRUNCATE_EXISTING)) {
            printDetails(seekableChannel, "Before writing data");
            writeData(seekableChannel, cs);
            printDetails(seekableChannel, "After writing data");
            seekableChannel.position(0);
            printDetails(seekableChannel, "After resetting position to 0");
            readData(seekableChannel, cs);
            printDetails(seekableChannel, "After reading data");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void writeData(SeekableByteChannel seekableChannel, Charset cs) throws IOException {
        String separator = System.getProperty("line.separator");
        StringBuilder sb = new StringBuilder();
        sb.append("test");
        sb.append(separator);
        sb.append("test2");
        sb.append(separator);

        CharBuffer charBuffer = CharBuffer.wrap(sb);
        ByteBuffer byteBuffer = cs.encode(charBuffer);
        seekableChannel.write(byteBuffer);
    }

    public static void readData(SeekableByteChannel seekableChannel, Charset cs) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(128);
        String encoding = System.getProperty("file.encoding");
        while (seekableChannel.read(byteBuffer) > 0) {
            byteBuffer.rewind();
            CharBuffer charBuffer = cs.decode(byteBuffer);
            System.out.print(charBuffer);
            byteBuffer.flip();
        }
    }

    public static void printDetails(SeekableByteChannel seekableChannel, String msg) {
        try {
            System.out.println(
                    msg + ": Size   = " + seekableChannel.size() + ", Position = " + seekableChannel.position());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面的程式碼生成以下結果。

Before writing data: Size   = 0, Position = 0
After writing data: Size   = 13, Position = 13
After resetting position to 0: Size   = 13, Position = 0
test
test2