28.

Java ファイル入出力の完全ガイド(NIO.2 / Files / Stream / try-with-resources)

編集
この記事の要点
  • Java 11+: 一発で読み書きできる Files.readString(path) / Files.writeString(path, str) を最優先
  • 行リストFiles.readAllLines / Files.writeAllLines大ファイルFiles.lines で Stream 化
  • Path / Paths: Paths.get("dir", "file.txt") でプラットフォーム非依存のパス構築
  • try-with-resources 必須: InputStream / Reader は自動 close
  • 文字コードは必ず明示: StandardCharsets.UTF_8。省略するとプラットフォーム既定で文字化け

結論: 用途別の推奨 API

やりたいこと推奨 APIJava 要件
テキスト全体を 1 文字列で読むFiles.readString(path)11+
テキスト全体を 1 文字列で書くFiles.writeString(path, str)11+
行ごとのリストで読むFiles.readAllLines(path, UTF_8)7+
行ごとのリストで書くFiles.write(path, lines, UTF_8)7+
巨大ファイルを 1 行ずつ Stream 処理Files.lines(path, UTF_8)8+
バイナリ全体Files.readAllBytes / Files.write7+
ファイルコピーFiles.copy(src, dst)7+

Java 11+ : 最も簡単な読み書き

import java.nio.file.*;
import java.nio.charset.StandardCharsets;

// テキストを 1 行で読み込む
String text = Files.readString(Paths.get("input.txt"));
// 文字コード指定 (デフォルトは UTF-8)
String text2 = Files.readString(Paths.get("input.txt"), StandardCharsets.UTF_8);

// テキストを 1 行で書き出す
Files.writeString(Paths.get("output.txt"), "Hello, World!");

// 上書きせず追記
Files.writeString(Paths.get("log.txt"), "new line\n",
    StandardOpenOption.APPEND, StandardOpenOption.CREATE);

行リストとして読み書き (Java 7+)

import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

// 全行をリストで読み込み
List<String> lines = Files.readAllLines(
    Paths.get("data.csv"), StandardCharsets.UTF_8);

for (String line : lines) {
    String[] cols = line.split(",");
    System.out.println(cols[0] + " : " + cols[1]);
}

// リストをファイルに書き出し
List<String> output = List.of("line1", "line2", "line3");
Files.write(Paths.get("output.txt"), output, StandardCharsets.UTF_8);

// 注意: readAllLines は全行をメモリに乗せる
// → 数百 MB 以上のファイルは Files.lines を使う

大ファイル: Files.lines で Stream 処理

import java.nio.file.*;
import java.util.stream.Stream;

// try-with-resources で Stream を必ず close
try (Stream<String> stream = Files.lines(
        Paths.get("bigfile.log"), StandardCharsets.UTF_8)) {

    long errorCount = stream
        .filter(l -> l.contains("ERROR"))
        .count();

    System.out.println("Errors: " + errorCount);
}

// 集計
try (Stream<String> stream = Files.lines(Paths.get("sales.csv"))) {
    double total = stream
        .skip(1)                                       // ヘッダー除外
        .mapToDouble(l -> Double.parseDouble(l.split(",")[2]))
        .sum();
    System.out.println("Total: " + total);
}

Path / Paths でパス構築

import java.nio.file.*;

// 絶対パス / 相対パス
Path p1 = Paths.get("/home/user/data.txt");
Path p2 = Paths.get("data", "in", "users.csv");          // OS の区切り文字で連結

// Java 11+ Path.of (同じ意味だが直感的)
Path p3 = Path.of("config", "app.yml");

// パス操作
Path p = Paths.get("/var/log/app/error.log");
p.getFileName();     // error.log
p.getParent();       // /var/log/app
p.getRoot();         // /
p.toAbsolutePath();  // 絶対パス化
p.normalize();       // ../ や ./ を解決

// 存在確認 / メタ情報
Files.exists(p);
Files.isDirectory(p);
Files.isRegularFile(p);
Files.size(p);                                          // バイト数
Files.getLastModifiedTime(p);

レガシー API: Reader / Writer + try-with-resources

細かい制御が必要な場合は BufferedReader / BufferedWriter を使います。必ず try-with-resources で自動 close:

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

// 行ごとに読み込み (メモリ効率良)
try (BufferedReader br = Files.newBufferedReader(
        Paths.get("input.txt"), StandardCharsets.UTF_8)) {
    String line;
    while ((line = br.readLine()) != null) {
        // 処理
    }
}

// 書き込み
try (BufferedWriter bw = Files.newBufferedWriter(
        Paths.get("output.txt"), StandardCharsets.UTF_8,
        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
    bw.write("line 1");
    bw.newLine();
    bw.write("line 2");
}

バイナリ入出力

import java.io.*;
import java.nio.file.*;

// 小〜中サイズ: 全バイト読み込み
byte[] data = Files.readAllBytes(Paths.get("image.png"));
Files.write(Paths.get("copy.png"), data);

// 大きいファイル: ストリームでチャンク読み込み
try (InputStream in = Files.newInputStream(Paths.get("big.bin"));
     OutputStream out = Files.newOutputStream(Paths.get("copy.bin"))) {
    byte[] buf = new byte[8192];
    int n;
    while ((n = in.read(buf)) != -1) {
        out.write(buf, 0, n);
    }
}

// Java 9+: 標準で transferTo
try (InputStream in = Files.newInputStream(Paths.get("big.bin"));
     OutputStream out = Files.newOutputStream(Paths.get("copy.bin"))) {
    in.transferTo(out);                                // 一発でコピー
}

文字コードの落とし穴

文字コードを省略するとプラットフォーム既定(Windows なら MS932/CP932、Linux は UTF-8)が使われます。Linux で動いていた処理が Windows で文字化けする典型例。必ず明示してください:

import java.nio.charset.StandardCharsets;

// ❌ 文字コード未指定 → プラットフォーム依存
String t = Files.readString(p);                        // OK だが既定 UTF-8 (Java 18+ から)

// ❌ FileReader は古い API でデフォルト依存
BufferedReader br = new BufferedReader(new FileReader("a.txt"));

// ✅ 明示
BufferedReader br = Files.newBufferedReader(p, StandardCharsets.UTF_8);
String t = Files.readString(p, StandardCharsets.UTF_8);

// Shift_JIS のファイルを読む
String t = Files.readString(p, java.nio.charset.Charset.forName("Shift_JIS"));

ファイル操作 (コピー / 移動 / 削除)

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

Path src = Paths.get("a.txt");
Path dst = Paths.get("b.txt");

// コピー (既存なら上書き)
Files.copy(src, dst, REPLACE_EXISTING);

// 移動 (リネーム含む)
Files.move(src, dst, REPLACE_EXISTING, ATOMIC_MOVE);

// 削除 (存在しないとエラー)
Files.delete(src);

// 存在チェック付き削除
Files.deleteIfExists(src);

// ディレクトリ作成
Files.createDirectories(Paths.get("dir/sub/nest"));   // 中間ディレクトリも作成

// ディレクトリ内のファイル列挙
try (Stream<Path> s = Files.list(Paths.get("/var/log"))) {
    s.filter(Files::isRegularFile)
     .filter(p -> p.toString().endsWith(".log"))
     .forEach(System.out::println);
}

// 再帰列挙
try (Stream<Path> s = Files.walk(Paths.get("/var/log"))) {
    s.filter(Files::isRegularFile).forEach(System.out::println);
}

FAQ

Q: FileNotFoundExceptionNoSuchFileException の違い?
A: 前者は旧 java.io 系、後者は新 java.nio.file 系の例外。Files API は NoSuchFileException をスロー。

Q: 改行コード (CRLF / LF) を制御したい
A: BufferedWriter.newLine() は OS 依存。明示的に bw.write("\n") と書く、または Files.writeString で改行込みの文字列を渡す。

Q: ファイルロックは?
A: FileChannel.tryLock() でアドバイザリロック。同一 JVM 内の並行制御には ReentrantLock 等を併用。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. データ型
  3. 変数
  4. 定数
  5. 配列
  6. コレクション(List,Set,Queue)
  7. Map(連想配列)
  8. 演算子
  9. 条件分岐
  10. 繰り返し制御文
  11. クラス
  12. メソッド
  13. インスタンス化
  14. コンストラクタ
  15. staticキーワード
  16. オーバーロード
  17. 継承
  18. オーバーライド
  19. this
  20. super
  21. パッケージ
  22. アクセス修飾子
  23. 抽象クラス・メソッド
  24. インターフェース
  25. カプセル化
  26. データベース接続
  27. セッション
  28. ファイル入出力
  29. ラムダ式

最近更新/作成されたページ