1. ファイル全体の読み取り
全バイトの読み取り
ときには、画像やアーカイブなどのバイナリ形式を問わず、ファイルの中身をそのまま一括で取り出したいことがあります。そのために Java には Files.readAllBytes(path) というメソッドがあります。これはバイト配列(byte[])を返し、ファイルの生データをそのまま扱えます。
import java.nio.file.*;
public class ReadBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
byte[] allBytes = Files.readAllBytes(path);
System.out.println("ファイルのバイト数: " + allBytes.length);
}
}
注意: ファイルが大きい(数GBなど)場合、この方法はメモリ不足を招く可能性があります。ファイル全体を一度にメインメモリへ読み込むためです.
全行の読み取り
テキストファイルには、より扱いやすい方法があります: Files.readAllLines(path)。これは行のリスト(List<String>)を返し、各要素がファイルの1行になります(行区切りは自動的に処理されます)。
import java.nio.file.*;
import java.util.List;
public class ReadLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("example.txt");
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
}
}
重要: 既定ではシステムのエンコーディングが使われます。エンコーディング(たとえば UTF-8)を明示したい場合は、オーバーロード版を使いましょう:
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
例: ファイルを読み込んで画面に出力する
これをミニアプリ(たとえば「メモ帳」や「ToDo リスト」)に組み込みましょう:
import java.nio.file.*;
import java.util.List;
public class TodoReader {
public static void main(String[] args) throws Exception {
Path todoPath = Paths.get("todo.txt");
if (Files.exists(todoPath)) {
List<String> tasks = Files.readAllLines(todoPath);
System.out.println("今日のタスク:");
for (String task : tasks) {
System.out.println("- " + task);
}
} else {
System.out.println("todo.txt が見つかりません。タスクリストを作成してください!");
}
}
}
2. ファイルへの書き込み
バイト配列の書き込み
バイト配列(たとえばシリアライズ結果、画像、音声など)がある場合は、Files.write(path, bytes) を使います。このメソッドはファイルがなければ作成し、あれば上書きします。
import java.nio.file.*;
public class WriteBytesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("output.bin");
byte[] data = {1, 2, 3, 4, 5};
Files.write(path, data);
System.out.println("バイト列を output.bin に書き込みました");
}
}
文字列リストの書き込み
テキストファイルには、行のリストを使うのが便利です。各要素が1行として書き込まれます。
import java.nio.file.*;
import java.util.Arrays;
import java.util.List;
public class WriteLinesExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("todo.txt");
List<String> tasks = Arrays.asList(
"牛乳を買う",
"おばあちゃんに電話する",
"Java の宿題をやる"
);
Files.write(path, tasks);
System.out.println("タスクリストを todo.txt に書き込みました");
}
}
注意: 既定ではファイルは上書きされます。すでに存在していた場合、古いタスクは消えます。(失わない方法については後で説明します。)
文字列をファイルに書く
1 行だけ書き込みたい場合は、1 要素のリストを作るか、Files.write(path, string.getBytes()) を使います:
import java.nio.file.*;
public class WriteStringExample {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
String message = "こんにちは、Java!";
Files.write(path, message.getBytes());
System.out.println("文字列を hello.txt に書き込みました");
}
}
文字エンコーディングを明示する
非 ASCII のテキストで文字化けを避けるため、常にエンコーディングを明示しましょう:
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
public class WriteLinesUtf8Example {
public static void main(String[] args) throws Exception {
Path path = Paths.get("hello.txt");
List<String> lines = List.of("こんにちは、世界!", "これは Java です。");
Files.write(path, lines, StandardCharsets.UTF_8);
}
}
3. エラー処理
ファイル操作では Java が IOException を投げることがあります。これは入出力レベルで「何かがうまくいかなかった」ことを示す一般的なシグナルです。原因はさまざまです。ファイルが存在しない、アクセス権がない、ディスクが一杯または使用中、さらには操作の最中に USB メモリが抜かれた、など。
そのため、読み書きメソッドを使うときは常にこうした状況に備える必要があります。これは try-catch で行います:
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class SafeReadExample {
public static void main(String[] args) {
Path path = Paths.get("todo.txt");
try {
List<String> lines = Files.readAllLines(path);
System.out.println("ファイルの内容:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException ex) {
System.out.println("ファイルの読み取り中にエラーが発生しました: " + ex.getMessage());
}
}
}
なぜこれが必要なのか?
try-catch は、潜在的なクラッシュを制御可能な状況に変えます。ファイルがないなら、ユーザーに落ち着いて知らせられます。アクセス不可なら、問題を指摘できます。操作の途中でディスクが消えても、プログラムはひどいスタックトレースを出して落ちるのではなく、少なくとも通常のメッセージで警告できます。言い換えると、IOException は敵ではなく、Java が「外部で何かがうまくいっていない。次にどうするか考えてくれ」と伝える手段にすぎません。
4. 実用例
ファイルを読み込み内容を表示する
import java.nio.file.*;
import java.util.List;
import java.io.IOException;
public class PrintFileExample {
public static void main(String[] args) {
Path path = Paths.get("notes.txt");
try {
if (!Files.exists(path)) {
System.out.println("notes.txt が見つかりません.");
return;
}
List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
System.out.println("notes.txt の内容:");
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("ファイルの読み取り中にエラーが発生しました: " + e.getMessage());
}
}
}
新しいファイルに文字列を書き込む
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
public class WriteFileExample {
public static void main(String[] args) {
Path path = Paths.get("greeting.txt");
String content = "Java IO の世界へようこそ!";
try {
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
System.out.println("文字列をファイル greeting.txt に書き込みました");
} catch (IOException e) {
System.out.println("ファイルの書き込み中にエラーが発生しました: " + e.getMessage());
}
}
}
文字列リストをファイルに書き込む
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.io.IOException;
public class WriteListExample {
public static void main(String[] args) {
Path path = Paths.get("shopping.txt");
List<String> items = List.of("パン", "牛乳", "チーズ");
try {
Files.write(path, items, StandardCharsets.UTF_8);
System.out.println("買い物リストを shopping.txt に書き込みました");
} catch (IOException e) {
System.out.println("ファイルの書き込み中にエラーが発生しました: " + e.getMessage());
}
}
}
5. ストリームの概要
Files.readAllBytes、Files.readAllLines、Files.write は「すべて一括」です。すなわち、ファイル全体をメモリに読み込むか、内容を一気にディスクへ書き込みます。小さなファイルには便利ですが、大きなファイルには向きません(OutOfMemoryError が出たり、巨大なログの読み込みで「固まる」ことがあります)。
大きなファイルや行単位の読み取りが必要な場合は、ストリームを使います:
- テキスト向け: BufferedReader、BufferedWriter
- バイト向け: InputStream、OutputStream
ストリームの詳細は次の講義で扱いますが、ここでは小さなティーザーを:
import java.nio.file.*;
import java.io.*;
public class BufferedReaderExample {
public static void main(String[] args) {
Path path = Paths.get("bigfile.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());
}
}
}
6. 役に立つポイント
- 上書き: Files.write は既定でファイルを上書きします。末尾に追記したい場合は追加オプションを使いましょう(詳細は次回)。
- エンコーディング: 非 ASCII テキストを扱うときは、常にエンコーディングを明示しましょう。出力の文字化けを防げます。良い選択は StandardCharsets.UTF_8 です。
- パス: クロスプラットフォーム性のために Paths.get(...) を使いましょう。スラッシュ(/ や \)を手書きでハードコードしないでください。
- ファイル名: 禁止文字(?、*、:、<、>、| など)はファイル名に使わないでください。特に Windows で動かすコードでは注意が必要です。
7. ファイルの読み書きでよくあるミス
誤り №1: IOException を処理していない。 多くの初心者は Files.readAllLines(path) を try-catch なしで書き、最初の問題(ファイルが見つからない、権限がない、ディスク破損など)でプログラムが落ちてしまいます。例外は必ず処理しましょう!
誤り №2: 大きなファイルに readAllBytes/readAllLines を使う。 ファイルが数百MB〜数GBある場合、丸ごと読み込もうとするとプログラムが「死に」かねません。そうしたケースではストリーム(BufferedReader)を使いましょう。
誤り №3: 文字列の読み書きでエンコーディングを指定していない。 エンコーディングを指定しないと、PC や OS によって結果が異なることがあります。特にキリル文字では問題が起きがちです。StandardCharsets.UTF_8 など、必要なエンコーディングを明示的に使いましょう。
誤り №4: File/Path が実体ファイルだと思っている。 File や Path はファイルそのものではなく、あくまで「ラベル」にすぎません。オブジェクトを作ってもディスク上にファイルは作られません。ファイルを作成するには Files.createFile、Files.write などのメソッドを使いましょう。
誤り №5: ストリームを閉じない(ストリームを使う場合)。 たとえば BufferedReader などのストリームを手動で使う場合は、必ずクローズしましょう(try-with-resources が最善)。そうしないと、ファイルがロックされたままになり、他のプログラムからアクセスできなくなることがあります。
GO TO FULL VERSION