Java | Java 標準ライブラリ:BufferedWriter

Java Java
スポンサーリンク

BufferedWriter を一言でいうと

BufferedWriter は、

「文字をいったんバッファ(メモリ)に貯めてから、まとめて出力ストリームに書き込むことで、高速にテキストを書き込むためのクラス」

です。

Writer をラップして使い、文字・文字配列・文字列を効率よく書き出せます。newLine() でプラットフォームに依存した改行も簡単に出せるようになっています。


BufferedWriter の役割と基本イメージ

なぜ「バッファ」が必要なのか

FileWriter などの Writer をそのまま使うと、write() を呼ぶたびに、文字がすぐ下流のストリーム(ファイルなど)へ送られる実装になりがちです。

1 文字や短い文字列を大量に書く場合、

  • OS への書き込み回数が非常に多くなる
  • ディスク I/O が増えて遅くなる

といった問題が出ます。

そこで BufferedWriter は、

  1. 内部に「文字のバッファ」を持つ
  2. write() が呼ばれるたびに、まずバッファに貯める
  3. バッファがいっぱいになったり、flush() / close() が呼ばれたタイミングで、一気に下流の Writer へ送る

という動きをします。

この「まとめ書き」によって、ディスクアクセスや出力ストリームへのアクセス回数が減り、パフォーマンスが大きく改善されます。

「Writer をラップする」クラスであること

BufferedWriter 自体は出力先を持っていません。
常に「別の Writer を包んで」使います。

例えば:

  • ファイルへ出力したい → new BufferedWriter(new FileWriter("foo.txt"))
  • OutputStream に書きたい → new BufferedWriter(new OutputStreamWriter(out))

というように、「どこに書くか」を担当する Writer を下流に持ち、その上にバッファリング機能をかぶせるイメージです。


基本的な使い方:ファイルにテキストを書く

最小構成のサンプル

ファイルに数行のテキストを書き込む基本例です。

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterBasic {
    public static void main(String[] args) {
        String fileName = "output.txt";

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
            writer.write("1行目のテキスト");
            writer.newLine();      // 改行を1つ出力
            writer.write("2行目のテキスト");
            writer.newLine();
            writer.write("3行目");
            // try-with-resources により、ここを抜けるときに自動で flush + close
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Java

ここで押さえておくポイントは次の通りです。

  • FileWriterBufferedWriter でラップしている
  • write(...) で文字列を書き、newLine() で改行を入れている
  • try-with-resources により、最後に自動で flush()close() が呼ばれる

特に newLine() は、System.getProperty("line.separator")(プラットフォームごとの改行文字列)に従って適切な改行を出力してくれるため、単に "\n" と書くよりも移植性が高いです。


重要メソッド:write / newLine / flush / close

write:文字列・文字配列を書き込む

BufferedWriterWriter を継承しているので、基本的な write メソッドを持ちます。

代表的なものは:

writer.write(int c);                // 1文字
writer.write(char[] cbuf);          // 文字配列
writer.write(char[] cbuf, int off, int len);
writer.write(String s);             // 文字列
writer.write(String s, int off, int len);
Java

write(String s) を使う場面が最も多いと思ってよいです。

内部的には、「すぐにディスクに行く」のではなく、「まずバッファに貯める」ことを意識しておくとよいです。

newLine:プラットフォーム依存の改行を出す

newLine() は、BufferedWriter 固有の便利メソッドです。

writer.newLine();
Java

これにより、

  • Windows なら \r\n
  • Unix / Linux / macOS なら \n

など、その環境の line.separator システムプロパティに基づく改行文字列が書き込まれます。

ドキュメントでも、「改行文字を直接書くより、newLine() を使うことが推奨される」とされています。
プラットフォーム間でログやファイルをやり取りするときにも、newLine() を使っておけば安心です。

flush:バッファの内容を強制的に下流へ流す

writer.flush();
Java

バッファに貯まっている文字を、強制的に下の Writer(ファイルなど)へ送ります。

通常、close() を呼べば内部で自動的に flush() されるので、
try-with-resources を使っている限り、明示的に flush() を呼ぶ必要はあまりありません。

ただし、

  • 長時間開きっぱなしで、途中経過を確実にディスクに書きたい
  • すぐ後に別プロセスがそのファイルを読むことが分かっている

といったケースでは、flush() を適切なタイミングで呼ぶことがあります。

close:リソース解放(必ず try-with-resources で)

close() は、バッファの flush を行ったうえで、下流の Writer を閉じます。

重要なのは、

  • BufferedWriter を閉じると、その下にある Writer(例:FileWriter)も一緒に閉じられる
  • 閉じた後に write() すると IOException になる

という点です。

現代の Java では、BufferedWriter を使うときは基本的に

try (BufferedWriter writer = new BufferedWriter(new FileWriter("..."))) {
    ...
}
Java

と書き、try-with-resources に閉じ処理を任せるのが標準的です。


バッファリングの効果をイメージで理解する

バッファなし書き込みとの違い

FileWriter だけを使うと、write("A") するたびに、
文字列がすぐにバイト列に変換され、ファイルへ書き込まれます。

例えば、1 文字ずつ 10 万回 write() した場合、

  • 10 万回の I/O 操作が発生
  • ディスクアクセスも多く、非常に非効率

となり得ます。

一方、BufferedWriter を挟むと、

  1. まずメモリ上のバッファに溜め込む
  2. バッファが満タンになったところで、まとめて下流へ書く

ので、実際の I/O 回数は大幅に減ります。
公式ドキュメントでも、「FileWriter や OutputStreamWriter など、write() のコストが高い Writer は BufferedWriter でラップすることが推奨される」と明記されています。

バッファサイズはどうなるか

BufferedWriter は、コンストラクタでバッファサイズを指定できます。

BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"), 8192);

サイズを指定しない場合は、適切とされるデフォルト値が使われます。
通常の用途では、デフォルトのままで十分です。


文字コード(エンコーディング)との関係

FileWriter は「プラットフォームのデフォルトエンコーディング」

new FileWriter("out.txt") は、内部で「プラットフォームのデフォルト文字コード」を使って文字をバイトに変換します。

  • Windows 日本語環境なら CP932(Shift_JIS 系)
  • Linux や macOS なら UTF-8

など、環境によって変わるため、コードだけ見ても何で書いているか分からないという弱点があります。

エンコーディングを明示するには OutputStreamWriter と組み合わせる

UTF-8 で確実に書き出したい場合は、次のようにします。

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

public class BufferedWriterUtf8 {
    public static void main(String[] args) {
        try (BufferedWriter writer =
                     new BufferedWriter(
                         new OutputStreamWriter(
                             new FileOutputStream("output.txt"),
                             StandardCharsets.UTF_8))) {

            writer.write("こんにちは UTF-8 の世界");
            writer.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Java

OutputStreamWriter にエンコーディングを渡し、
それをさらに BufferedWriter でラップしています。

まとめると、

  • 文字コードを意識しない簡易用途 → new BufferedWriter(new FileWriter(...))
  • 文字コードをきちんと指定したい用途 → BufferedWriter + OutputStreamWriter + FileOutputStream

という整理になります。


BufferedWriter を使うときに意識したいこと

常に try-with-resources で使う

BufferedWriterAutoCloseable なので、必ず close() が必要です。
書き忘れを防ぐためにも、try-with-resources で書くのを習慣にしてください。

try (BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"))) {
    // 書き込み処理
}
Java

これだけで、

  • 正常終了時も
  • 例外発生時も

必ず flush()close() が呼ばれます。

「バッファにあるうちはファイルに見えない」ことを意識する

バッファに溜まっている間は、物理ファイルにはまだ書かれていません。

  • すぐに別プロセスがそのファイルを読む
  • ログをリアルタイムに tail したい

などのケースでは、

  • 適宜 flush() を呼ぶ
  • ある程度まとめて書く設計にする

などを考えた方が良いです。


まとめ:BufferedWriter を自分の中でこう位置づける

BufferedWriter を初心者向けにまとめると、

「別の Writer をラップし、内部バッファに文字を貯めてからまとめて出力することで、高速かつ便利にテキストを書き込めるクラス」

です。

特に意識しておきたい点は:

  • 常に何かの Writer(FileWriter / OutputStreamWriter など)をラップして使う
  • 内部バッファを使って書き込み回数を減らし、性能を改善する
  • write(...)newLine() が基本コンボで、newLine() は環境依存の改行を出してくれる
  • 必ず try-with-resources で使い、flush/close を自動化する
  • エンコーディングを意識する場合は OutputStreamWriter との組み合わせが基本
タイトルとURLをコピーしました