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