概要

業務システムでは、ログの出力、バッチ処理の中間ファイル生成、他システムとの連携ファイル作成など、テキストファイルの読み書きを避けて通れません。Java の java.io パッケージには BufferedReader や BufferedWriter といった基本的なクラスが揃っていますが、文字コードの指定を忘れて文字化けを起こしたり、close 漏れでファイルが壊れたりといったトラブルが後を絶ちません。この記事では、UTF-8 を明示した安全な入出力パターンを整理し、追記モード、バイナリコピー、ファイルの存在確認やサイズ取得までを実務で再利用できる形にまとめます。

使いどころ

バッチ処理の実行ログを日付ごとのテキストファイルに出力する

外部システムから受け取ったテキストファイルを1行ずつ読み込んでDB登録する

帳票データを一時ファイルに書き出し、後続の帳票生成プロセスに渡す

コード例

UTF-8 テキストファイルの読み書き
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;

public class FileIoBasics {

    /** テキストファイルを UTF-8 で1行ずつ読み込む */
    public static List<String> readLines(String filePath) throws IOException {
        var lines = new ArrayList<String>();
        try (var reader = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream(filePath), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }
        return lines;
    }

    /** テキストファイルに UTF-8 で書き込む */
    public static void writeLines(String filePath, List<String> lines) throws IOException {
        try (var writer = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(filePath), StandardCharsets.UTF_8))) {
            for (var line : lines) {
                writer.write(line);
                writer.newLine();
            }
        }
    }

    /** 追記モードで1行追加 */
    public static void appendLine(String filePath, String line) throws IOException {
        try (var writer = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(filePath, true), StandardCharsets.UTF_8))) {
            writer.write(line);
            writer.newLine();
        }
    }

    public static void main(String[] args) throws IOException {
        var path = System.getProperty("java.io.tmpdir") + "/sample.txt";

        writeLines(path, List.of("1行目: Hello", "2行目: ファイルI/O", "3行目: UTF-8"));
        System.out.println("書き込み完了: " + path);

        var lines = readLines(path);
        lines.forEach(l -> System.out.println("  " + l));

        appendLine(path, "4行目: 追記");
        System.out.println("追記後: " + readLines(path).size() + "行");

        new File(path).delete();
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

var による型推論で記述量が減る。java.io の API 自体に変更はないが、List.of() との組み合わせで簡潔に書ける。

Java 17
// Java 17: var で型推論、List.of() で簡潔に
var reader = new BufferedReader(
    new InputStreamReader(
        new FileInputStream(path), StandardCharsets.UTF_8));
writeLines(path, List.of("1行目", "2行目", "3行目"));

Library Comparison

Pure Java(java.io)基本的なテキスト/バイナリの読み書き。外部依存を増やしたくない場合。NIO に比べてコード量が多い。パス操作やファイル属性取得が冗長になる。
java.nio.file.FilesJava 7 以降で新規コードを書く場合。readAllLines / writeString など1行で完結する操作が多い。小〜中規模ファイル向け。大容量ファイルは Files.lines() のストリーム処理に切り替える必要がある。
Apache Commons IOFileUtils.readFileToString() などの便利メソッドが欲しい場合。外部依存が増える。標準 API で十分な場面では過剰。

注意点

FileReader/FileWriter はデフォルトでプラットフォームのエンコーディングを使うため、環境によって文字化けする。必ず InputStreamReader + StandardCharsets.UTF_8 で文字コードを明示すること。

try-with-resources を使わないと、例外発生時にストリームが close されずファイルが中途半端な状態になる。

FileOutputStream の append フラグ(第2引数 true)を指定し忘れると、既存ファイルの内容が上書きで消える。

大容量ファイルを List に全行読み込むとメモリを圧迫する。数十万行を超える場合はストリーム処理を検討すること。

FAQ

FileReader と InputStreamReader のどちらを使うべきですか。

FileReader は Java 11 以降で文字コード指定のコンストラクタが追加されましたが、Java 8 との互換性を考えると InputStreamReader + StandardCharsets.UTF_8 が無難です。

NIO の Files と java.io のどちらを選ぶべきですか。

新規コードでは Files を推奨します。既存コードが java.io ベースなら無理に書き換える必要はありません。

バイナリファイルのコピーにバッファサイズはどのくらいが適切ですか。

8KB(8192バイト)が一般的です。大容量ファイルなら 64KB〜1MB に増やすとスループットが向上する場合があります。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます