Java:ファイル入出力
1学習の目的
- ファイルの読み書きができるようになる。プログラムを終了してもデータが残る——ここで初めて実用的なアプリケーションに近づく。
- try-with-resources でリソースを確実に閉じられるようになる。ファイルの閉じ忘れという、実務で深刻な問題を防げるようになる。
2基礎解説
これまでのプログラムは、終了するとデータが消えていました。ファイルに保存すれば、次回起動時に読み込める——CSVの取り込み、ログの出力、設定の保存など、実務では必須の操作です。
| やりたいこと | 書き方 | 備考 |
|---|---|---|
| 書き込む | Files.writeString(path, text) | 上書き |
| 全部読む | Files.readString(path) | 小さいファイル向け |
| 行ごとに読む | Files.readAllLines(path) | List で返る |
| 存在確認 | Files.exists(path) | 読む前に確認 |
- ファイル操作は必ず IOException を伴う。try-catch で囲むか、throws IOException を宣言する必要がある——失敗を無視できない設計になっている。
- Path.of("ファイル名") でパスを作る。現代のJavaでは Files クラスと組み合わせるのが標準的な書き方。
- ⚠ writeString は既存の内容を消して上書きする。追記したいなら StandardOpenOption.APPEND を渡す。
- ⭐ try-with-resources なら自動で閉じてくれる。try (リソース) { … } と書けば、正常終了でも例外発生でも確実にクローズされる。
- 存在しないファイルを読むと NoSuchFileException。読む前に Files.exists() で確認するか、catch で対処する。
現場使用例:CSVの取り込み、ログの記録、設定ファイルの読み込み、レポートの出力。業務システムの多くはファイルを介してデータをやり取りする。
import java.io.*;
import java.nio.file.*;
import java.util.*;
// ① 書き込む(上書き)
Path path = Path.of("data.txt");
Files.writeString(path, "1行目\n2行目\n");
// ② 追記する
Files.writeString(path, "3行目\n", StandardOpenOption.APPEND);
// ③ 全体を読む
String all = Files.readString(path);
// ④ 行ごとに読む(List で返る)
List<String> lines = Files.readAllLines(path);
for (String line : lines) {
System.out.println(line);
}
// ⑤ 存在確認と削除
Files.exists(path); // true / false
Files.deleteIfExists(path); // あれば削除
// ⑥ ⭐ try-with-resources(自動で閉じる)
try (BufferedWriter w = Files.newBufferedWriter(Path.of("out.txt"))) {
w.write("大量のデータを書くとき向き");
w.newLine();
} // ← ここで自動的に閉じられる
// ⑦ 例外への対処は必須
try {
String text = Files.readString(Path.of("nothing.txt"));
} catch (NoSuchFileException e) {
System.out.println("ファイルがありません");
} catch (IOException e) {
System.out.println("読み込みに失敗しました");
}
// ⑧ CSVの処理(定番パターン)
for (String line : Files.readAllLines(Path.of("data.csv"))) {
String[] parts = line.split(",");
String name = parts[0];
int price = Integer.parseInt(parts[1]);
}
3基本ドリル(10問)
出力 == こんにちは
Files.writeString と Files.readString を使う。main に throws IOException を付ける。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("test.txt");
Files.writeString(path, "こんにちは");
System.out.println(Files.readString(path));
Files.deleteIfExists(path);
}
}書いたものがファイルに残る。プログラムを終了しても消えないので、次回起動時に読み込める。
A. 何も不要 B. IOException への対処 C. static の宣言 D. final の宣言 選択★☆☆無料
解答 == B
ファイル操作は失敗する可能性がある。
B
try-catch で囲むか throws を宣言する。Javaは「失敗しうる操作」への対処を強制するので、書き忘れるとコンパイルが通らない。
出力 == 3
Files.readAllLines は List
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("lines.txt");
Files.writeString(path, "1行目\n2行目\n3行目\n");
List<String> lines = Files.readAllLines(path);
System.out.println(lines.size());
Files.deleteIfExists(path);
}
}readAllLines は改行で分割して List にする。CSVやログの処理では、行単位で扱うことがほとんど。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.____("sample.txt");
Files.writeString(path, "OK");
System.out.println(Files.readString(path));
Files.deleteIfExists(path);
}
}出力 == OK
「〜の」を意味する2文字のメソッド。
of
Path.of が現代的な書き方。古いコードでは new File("…") を見かけるが、新規なら Path を使う。
A. 追記される B. 上書きされる C. エラー D. 2つのファイルができる 選択★☆☆無料
解答 == B
既定の動作は上書き。
B
前の内容が消える。追記したいなら StandardOpenOption.APPEND を第3引数に渡す。
出力 == 3
StandardOpenOption.APPEND を第3引数に渡す。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("append.txt");
Files.writeString(path, "1行目\n2行目\n");
Files.writeString(path, "3行目\n", StandardOpenOption.APPEND);
System.out.println(Files.readAllLines(path).size());
Files.deleteIfExists(path);
}
}ログの記録では追記が基本。上書きしてしまうと、過去の記録がすべて失われる。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("check.txt");
Files.writeString(path, "test");
System.out.println(Files.____(path));
Files.deleteIfExists(path);
}
}出力 == true
「存在する」を意味する6文字のメソッド。
exists
読む前に確認すれば例外を避けられる。ただし確認と読み込みの間にファイルが消える可能性もあるので、catch も併用するのが確実。
A. 速くなる B. 自動でリソースを閉じてくれる C. 例外が出ない D. メモリを使わない 選択★★☆広告解放
解答 == B
finally で close を書く手間が省ける。
B
正常終了でも例外発生でも確実に閉じられる。閉じ忘れるとファイルがロックされたままになり、他の処理が失敗する。
出力 == ファイルがありません
NoSuchFileException を catch する。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) {
try {
Files.readString(Path.of("nothing_xyz.txt"));
} catch (NoSuchFileException e) {
System.out.println("ファイルがありません");
} catch (IOException e) {
System.out.println("読み込み失敗");
}
}
}ファイルが無いのは「よくあること」。初回起動時は設定ファイルが存在しないので、この対処は必須になる。
出力 == 自動で閉じる
try (BufferedWriter w = Files.newBufferedWriter(path)) { ... } と書く。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("writer.txt");
try (BufferedWriter w = Files.newBufferedWriter(path)) {
w.write("自動で閉じる");
}
System.out.println(Files.readString(path));
Files.deleteIfExists(path);
}
}カッコを抜けた時点で自動的に閉じられる。finally { w.close(); } を書く必要がなく、閉じ忘れも起きない。
4実践シナリオ(5問)
出力 == 774400
split(",") で分割し、parseInt で数値に変換する。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("stock.csv");
Files.writeString(path, "ノートPC,128000,5\nマウス,3200,42\n");
int total = 0;
for (String line : Files.readAllLines(path)) {
String[] parts = line.split(",");
total += Integer.parseInt(parts[1]) * Integer.parseInt(parts[2]);
}
System.out.println(total);
Files.deleteIfExists(path);
}
}CSVの取り込みはこの形が基本。実務では業務システム間のデータ連携で頻繁に使われる。
出力3行
APPEND オプションで繰り返し追記する。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("log.txt");
Files.writeString(path, "");
for (int i = 1; i <= 3; i++) {
Files.writeString(path, "処理" + i + "\n",
StandardOpenOption.APPEND);
}
for (String line : Files.readAllLines(path)) {
System.out.println(line);
}
Files.deleteIfExists(path);
}
}最初に空で書いてファイルを初期化している。APPEND だけだと、前回実行時のログが残ったまま追記されてしまう。
出力2行。データあり / 未作成
Files.exists で分岐する。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path exist = Path.of("exist.txt");
Path notExist = Path.of("not_exist_xyz.txt");
Files.writeString(exist, "データあり");
System.out.println(Files.exists(exist)
? Files.readString(exist) : "未作成");
System.out.println(Files.exists(notExist)
? Files.readString(notExist) : "未作成");
Files.deleteIfExists(exist);
}
}初回起動時はファイルが無いのが普通。「無ければ既定値を使う」という処理が、設定ファイルの読み込みでは定番になる。
出力 == 在庫総額:774,400円
読み込んで計算し、結果を writeString で別ファイルに書く。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path input = Path.of("in.csv");
Path output = Path.of("report.txt");
Files.writeString(input, "ノートPC,128000,5\nマウス,3200,42\n");
int total = 0;
for (String line : Files.readAllLines(input)) {
String[] parts = line.split(",");
total += Integer.parseInt(parts[1]) * Integer.parseInt(parts[2]);
}
Files.writeString(output,
"在庫総額:" + String.format("%,d", total) + "円");
System.out.println(Files.readString(output));
Files.deleteIfExists(input);
Files.deleteIfExists(output);
}
}読み込み → 加工 → 書き出しがバッチ処理の基本形。夜間に大量のデータを処理するシステムは、この流れで動いている。
出力 == 131200
行ごとに try-catch し、失敗したら continue する(JV13の応用)。
import java.io.*;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Path path = Path.of("mixed.csv");
Files.writeString(path, "128000\nabc\n3200\n");
int total = 0;
for (String line : Files.readAllLines(path)) {
try {
total += Integer.parseInt(line.trim());
} catch (NumberFormatException e) {
continue;
}
}
System.out.println(total);
Files.deleteIfExists(path);
}
}1行の不正で全体を止めない。実際のデータには空行や見出し行が混ざることが多いので、この対処は実務で必須になる。
5仕上げ課題
入力ファイル stock.csv(プログラム内で作成)
ノートPC,128000,5
マウス,3200,42
不正行,abc,10
モニター,45000,0
キーボード,8500,18
処理
① CSVを読み込み、数値変換に失敗した行はスキップ(件数を数える)
② 各商品を「ノートPC:640,000円(残りわずか)」の形式で出力
・在庫0なら「品切れ」、10未満なら「残りわずか」、それ以外「在庫あり」
③ 在庫総額を計算
④ 結果を report.txt に書き出し、読み返して出力
⑤ 最後に作成したファイルを削除
期待される出力(7行)
ノートPC:640,000円(残りわずか)
マウス:134,400円(在庫あり)
モニター:0円(品切れ)
キーボード:153,000円(在庫あり)
---
取込4件 / 除外1件
在庫総額:927,400円
※ 最後の2行は report.txt から読み返した内容とする 総合★★★広告解放
出力7行が完全一致
行ごとに try-catch して NumberFormatException をスキップする。レポートは StringBuilder か文字列連結で組み立て、writeString で保存してから readString で読み返す。
import java.io.*;
import java.nio.file.*;
public class Main {
static String label(int stock) {
if (stock == 0) return "品切れ";
if (stock < 10) return "残りわずか";
return "在庫あり";
}
public static void main(String[] args) throws IOException {
Path input = Path.of("stock.csv");
Path output = Path.of("report.txt");
Files.writeString(input,
"ノートPC,128000,5\n"
+ "マウス,3200,42\n"
+ "不正行,abc,10\n"
+ "モニター,45000,0\n"
+ "キーボード,8500,18\n");
int total = 0;
int ok = 0;
int ng = 0;
for (String line : Files.readAllLines(input)) {
String[] parts = line.split(",");
int price;
int stock;
try {
price = Integer.parseInt(parts[1].trim());
stock = Integer.parseInt(parts[2].trim());
} catch (NumberFormatException e) {
ng++;
continue;
}
int value = price * stock;
total += value;
ok++;
System.out.println(parts[0] + ":"
+ String.format("%,d", value) + "円(" + label(stock) + ")");
}
String report = "取込" + ok + "件 / 除外" + ng + "件\n"
+ "在庫総額:" + String.format("%,d", total) + "円";
Files.writeString(output, report);
System.out.println("---");
System.out.println(Files.readString(output));
Files.deleteIfExists(input);
Files.deleteIfExists(output);
}
}このプログラムは、実務のバッチ処理そのものだ。ファイルを読み、不正データを除外しながら集計し、結果をレポートとして書き出す——夜間に動く業務システムの多くが、この構造をしている。
不正行の扱いが最も重要だ。「abc」を数値に変換できないからといって処理全体を止めてしまえば、4件の正常なデータも失われる。かといって黙って無視すると、なぜ件数が合わないのか誰にも分からない。スキップしつつ件数を記録する——この折衷案が、実務では最も現実的な対処になる。
parts[1].trim() の trim() も見逃せない。CSVは人間が編集することも多く、カンマの後ろに空白が入っているのは日常茶飯事だ。JV03で学んだこの処理が、ここで効いてくる。
そして結果をファイルに書き出している点。画面に出力するだけなら、プログラムが終わった瞬間に消える。ファイルに残せば、翌朝担当者が確認できるし、過去の記録と比較することもできる。
ただし今回のコードには課題もある。parts[1] と書いているが、もし列が2つしかない行があれば範囲外エラーで落ちる。実務ではここも検証が必要になる——外部から来るデータは、あらゆる想定外を含んでいると考えるべきだ。
次章ではジェネリクスを学ぶ。List<String> の <> が何なのか、その仕組みを理解する。