【Java】Javaのjava.nio.fileでファイル入出力を行う基本

Java

こんにちは、かつコーチです。
「ファイルを読み書きしたいだけなのに、FileReaderBufferedReaderを何行も書くのが面倒」と感じたことはありませんか。
今回はJava 7以降の標準であるjava.nio.file(新しいファイル操作APIパッケージ)を使った、シンプルなファイル入出力の書き方を解説します。

java.nio.fileとは?

従来のjava.ioとの違い

Javaには古くからjava.io.Fileによるファイル操作がありますが、java.nio.fileはより簡潔で高機能なAPIとして追加されました。

  • java.io.File(従来のファイル操作クラス):存在確認や削除はできるが、読み書きは別のクラスと組み合わせる必要がある
  • Path(ファイルやディレクトリの場所を表すクラス):java.nio.fileでの場所指定の基本単位
  • Files(ファイル操作のユーティリティクラス):読み書き・コピー・削除などをまとめて提供する

java.nio.fileでは「場所(Path)」と「操作(Files)」を分けて考えるのが特徴です。

なぜjava.nio.fileが使いやすいのか

Filesクラスの静的メソッドを使うと、ファイル全体の読み込みや書き込みが1行で書けます。
ストリームを開いて閉じる処理を意識しなくてよいため、初中級者でも扱いやすいのがメリットです。

基本の書き方

ファイルの読み込み

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class FileReadSample {
    public static void main(String[] args) {
        Path path = Path.of("sample.txt");

        try {
            List<String> lines = Files.readAllLines(path);
            for (String line : lines) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("ファイルの読み込みに失敗しました: " + e.getMessage());
        }
    }
}

Path.ofでファイルの場所を指定し、Files.readAllLinesで全行をList<String>として一括取得できます。
ファイル操作は失敗する可能性があるため、IOExceptiontry-catchで処理する必要がある点に注意しましょう。

ファイルへの書き込み

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;

Path path = Path.of("output.txt");
List<String> lines = List.of("1行目です", "2行目です", "3行目です");

try {
    Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (IOException e) {
    System.out.println("書き込みに失敗しました: " + e.getMessage());
}

Files.writeList<String>をそのまま渡すだけで、改行区切りでファイルに書き込んでくれます。
StandardOpenOption.CREATE(ファイルがなければ作成)とStandardOpenOption.APPEND(末尾に追記)を組み合わせるのが実務でよく使うパターンです。

ファイル・ディレクトリの存在確認と操作

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

Path dir = Path.of("logs");

if (!Files.exists(dir)) {
    Files.createDirectory(dir);
}

Path file = dir.resolve("app.log");
System.out.println(Files.exists(file)); // false(まだ作成前なら)

Files.existsで存在確認、Files.createDirectoryでディレクトリ作成ができます。
resolveメソッドを使うと、OSごとの区切り文字(/\か)を意識せずにパスを組み立てられるのも大きな利点です。

よくあるつまずきポイント・エラー対処

相対パスの基準ディレクトリを勘違いした

実際に私がハマった経験ですが、IDEから実行するときと、コマンドラインからjarファイルを実行するときで、相対パスの基準ディレクトリが違って「ファイルが見つからない」エラーに悩まされたことがあります。

// ❌Before:相対パスだけで指定して環境によって失敗する
Path path = Path.of("config/settings.txt");
// NoSuchFileException: config/settings.txt

このコードは実行時のカレントディレクトリ(user.dirシステムプロパティ)を基準に解決されるため、IDEとターミナルで実行場所が違うとNoSuchFileExceptionになります。

// ✅After:絶対パスへ変換して確認できるようにする
Path path = Path.of("config/settings.txt").toAbsolutePath();
System.out.println("読み込み先: " + path); // 実際に見ている場所を出力して確認する

if (!Files.exists(path)) {
    throw new IllegalStateException("設定ファイルが見つかりません: " + path);
}

toAbsolutePath()で実際に解決されるパスを出力すると、環境差によるファイル未検出のトラブルを早期に発見できます。

応用・一歩先の使い方

try-with-resourcesとBufferedReaderの組み合わせ

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

Path path = Path.of("large-data.txt");

try (BufferedReader reader = Files.newBufferedReader(path)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.out.println("読み込みエラー: " + e.getMessage());
}

大きなファイルを扱うときはreadAllLinesで全部メモリに読み込むのではなく、Files.newBufferedReadertry-with-resources(リソースを自動でクローズする構文)を組み合わせて1行ずつ処理する方がメモリ効率がよくなります。

ディレクトリ内のファイル一覧を取得する

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

Path dir = Path.of(".");

try (Stream<Path> paths = Files.list(dir)) {
    paths.filter(Files::isRegularFile)
         .forEach(System.out::println);
} catch (IOException e) {
    System.out.println("一覧取得に失敗しました: " + e.getMessage());
}

Files.listはStream APIと組み合わせられるため、filterで条件を絞り込んだファイル一覧処理もシンプルに書けます。

まとめ

この記事のポイント

  • java.nio.fileはPath(場所)とFiles(操作)を分けて考える設計
  • Files.readAllLinesFiles.writeで少量のファイル読み書きが簡潔に書ける
  • 相対パスは実行環境によって基準が変わるため、toAbsolutePath()で確認する習慣をつける
  • 大きなファイルはBufferedReadertry-with-resourcesで1行ずつ処理する

次に読むべき記事

  • java.timeで日付時刻を扱う
  • JDBCでデータベースに接続する基本

タグ: Java, 中級者向け, 標準ライブラリ

タイトルとURLをコピーしました