こんにちは、かつコーチです。
「ファイルを読み書きしたいだけなのに、FileReaderやBufferedReaderを何行も書くのが面倒」と感じたことはありませんか。
今回は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>として一括取得できます。
ファイル操作は失敗する可能性があるため、IOExceptionをtry-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.writeはList<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.newBufferedReaderとtry-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.readAllLines・Files.writeで少量のファイル読み書きが簡潔に書ける- 相対パスは実行環境によって基準が変わるため、
toAbsolutePath()で確認する習慣をつける - 大きなファイルは
BufferedReader+try-with-resourcesで1行ずつ処理する
次に読むべき記事
- java.timeで日付時刻を扱う
- JDBCでデータベースに接続する基本
タグ: Java, 中級者向け, 標準ライブラリ