Java : ファイル操作の基本
ファイルの読み込み、書き込み、削除、コピー…ディレクトリの作成・削除などなど。
ファイル操作の基本をコード例つきでご紹介します。
注意
- 本記事のコード例はWindows10で実行しています。
基本
ファイル操作の基本は、
- Pathインタフェースで操作対象のファイルの場所を指定
- Filesクラスで実際の操作
となります。
リソース解放は忘れずに
基本といいつつ少し難しめの内容で申し訳ないのですが、とても重要なので説明します。
ファイルを操作するAPIには、AutoCloseableを実装しているクラスやインタフェースがいくつかあります。
具体的には、本記事では以下のAPIが対象となります。
これらのAPIは使い終わったら 必ずclose を呼び出して、リソースを解放しなければなりません。
解放を忘れるとリソースリークという問題が発生します。
できれば try-with-resources文 を使って解放しましょう。
もう少し詳しいことは下記の記事にもまとめました。
よろしければご参照ください。
パス
Path API | 説明 |
---|---|
Path of (String first, String... more) | パス文字列からPathを生成します。 |
ファイルを操作するには、対象となるファイルの場所を指定する必要があります。
そのためには、ファイルやディレクトリの位置を特定する Pathインタフェース を使います。
これはファイルやディレクトリが実際にそこに存在していなくても問題ありません。
// ※相対パスは、Javaを実行したときのカレントディレクトリからのパスとなります。
// 相対パスでファイルを指定
final var file1 = Path.of("aaa.txt");
System.out.println(file1); // "aaa.txt"
// サブディレクトリにあるファイルを指定
final var file2 = Path.of("dir", "bbb.txt");
System.out.println(file2); // "dir\bbb.txt"
// 1つ上のディレクトリにあるファイルを指定
final var file3 = Path.of("..", "ccc.txt");
System.out.println(file3); // "..\ccc.txt"
// 絶対パスでファイルを指定
final var file = Path.of("D:", "java-work", "aaa.txt");
System.out.println(file); // "D:\java-work\aaa.txt"
ディレクトリも Pathインタフェース で指定します。
// 相対パスでディレクトリを指定
final var dir1 = Path.of("aaa-dir");
System.out.println(dir1); // "aaa-dir"
// サブディレクトリにあるディレクトリを指定
final var dir2 = Path.of("aaa-dir", "bbb-dir");
System.out.println(dir2); // "aaa-dir\bbb-dir"
// 1つ上のディレクトリにあるディレクトリを指定
final var dir3 = Path.of("..", "ccc-dir");
System.out.println(dir3); // "..\ccc-dir"
// 絶対パスでディレクトリを指定
final var dir = Path.of("D:", "java-work", "aaa-dir");
System.out.println(dir); // "D:\java-work\aaa-dir"
存在チェック
Files API | 説明 |
---|---|
boolean exists (Path path, LinkOption... options) | パスが存在するかどうかチェックします。 ファイルでもディレクトリでも存在すればtrueを返します。 |
※ファイル構成の確認には Windows の PowerShell を使っています。
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//d----- 2021/04/23 00:00 aaa-dir
//-a---- 2021/04/23 00:00 17 aaa.txt
aaa.txt ファイルと aaa-dir ディレクトリが存在する状態です。
final var file1 = Path.of("D:", "java-work", "aaa.txt");
final var file2 = Path.of("D:", "java-work", "bbb.txt");
System.out.println(file1); // D:\java-work\aaa.txt
System.out.println(file2); // D:\java-work\bbb.txt
System.out.println(Files.exists(file1)); // true
System.out.println(Files.exists(file2)); // false
final var dir1 = Path.of("D:", "java-work", "aaa-dir");
final var dir2 = Path.of("D:", "java-work", "bbb-dir");
System.out.println(dir1); // D:\java-work\aaa-dir
System.out.println(dir2); // D:\java-work\bbb-dir
System.out.println(Files.exists(dir1)); // true
System.out.println(Files.exists(dir2)); // false
ファイル
文字列の書き込み・読み込み
文字列の書き込み・読み込みのAPIは大きくわけて2種類あります。
- ファイルサイズが小さい場合 (一度に読み込んでもメモリ不足にならないサイズ)
- ファイルサイズが大きい場合 (一度に読み込むとメモリ不足になるサイズ)
使用場面 | Files API | 説明 |
---|---|---|
ファイルサイズが小さい場合 | Path writeString (Path path, CharSequence csq, OpenOption... options) | ファイルに文字列を一度に書き込みます。 |
String readString (Path path) | ファイルから一度に全ての文字列を読み込みます。 | |
ファイルサイズが大きい場合 | BufferedWriter newBufferedWriter (Path path, OpenOption... options) | ファイルへ文字列を書き込みます。 |
BufferedReader newBufferedReader (Path path) | ファイルから文字列を読み込みます。 |
基本的には writeString、readString がおすすめです。
リソース解放をする必要がありません。
final var file = Path.of("D:", "java-work", "aaa.txt");
System.out.println(file); // D:\java-work\aaa.txt
final var csq = """
abcde
YYY
ZZZ
""";
final var ret = Files.writeString(file, csq);
System.out.println(ret); // D:\java-work\aaa.txt
// --- PowerShell ---
//PS D:\java-work> cat .\aaa.txt
//abcde
//YYY
//ZZZ
Files.writeString を使って、aaa.txt に
abcde
YYY
ZZZ
という文字列が書き込まれました。
次に、readString を使って、書き込んだ文字列を読み込んでみましょう。
final var ret = Files.readString(file);
System.out.println(ret);
// 結果
// ↓
//abcde
//YYY
//ZZZ
これで文字列の基本的な読み書きができました。
ただし、大きなサイズのファイルは一度に読み込むとメモリ不足になる可能性があります。
そのような場面では、BufferedReader を使い少しずつ読み込みましょう。
書き込みについても同じです。
final var file = Path.of("D:", "java-work", "ccc.txt");
System.out.println(file); // D:\java-work\ccc.txt
try (final var writer = Files.newBufferedWriter(file)) {
writer.write("abcde");
// 改行を書き込みます。
writer.newLine();
writer.write("YYY");
writer.newLine();
writer.write("ZZZ");
}
// --- PowerShell ---
//PS D:\java-work> cat .\ccc.txt
//abcde
//YYY
//ZZZ
try (final var reader = Files.newBufferedReader(file)) {
//abcde
System.out.println(reader.readLine());
//YYY
System.out.println(reader.readLine());
//ZZZ
System.out.println(reader.readLine());
}
BufferedWriter と BufferedReader はリソース解放のために必ずcloseしましょう。
できれば try-with-resources文 を使いましょう。
バイトデータの書き込み・読み込み
バイトデータの書き込み・読み込みのAPIは大きくわけて2種類あります。
- ファイルサイズが小さい場合 (一度に読み込んでもメモリ不足にならないサイズ)
- ファイルサイズが大きい場合 (一度に読み込むとメモリ不足になるサイズ)
使用場面 | Files API | 説明 |
---|---|---|
ファイルサイズが小さい場合 | Path write (Path path, byte[] bytes, OpenOption... options) | ファイルにバイトデータを一度に書き込みます。 |
byte[] readAllBytes (Path path) | ファイルから一度に全てのバイトデータを読み込みます。 | |
ファイルサイズが大きい場合 | OutputStream newOutputStream (Path path, OpenOption... options) | ファイルにバイトデータを書き込みます。 |
InputStream newInputStream (Path path, OpenOption... options) | ファイルからバイトデータを読み込みます。 |
基本的には write、readAllBytes がおすすめです。
リソース解放をする必要がありません。
final var file = Path.of("D:", "java-work", "aaa.data");
System.out.println(file); // D:\java-work\aaa.data
final byte[] bytes = {1, 2, 4, 8, 16};
final var ret = Files.write(file, bytes);
System.out.println(ret); // D:\java-work\aaa.data
// ※バイナリファイルの確認
// --- PowerShell ---
//PS D:\java-work> Format-Hex .\aaa.data
//
// パス: D:\java-work\aaa.data
//
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
//
//00000000 01 02 04 08 10
Files.write を使い、aaa.data ファイルにバイナリデータ が書き込めました。
次に、Files.readAllBytes を使って、書き込んだバイナリデータを読み込んでみましょう。
final var ret = Files.readAllBytes(file);
System.out.println(Arrays.toString(ret)); // [1, 2, 4, 8, 16]
これでバイナリデータの基本的な読み書きができました。
ただし、大きなサイズのファイルは一度に読み込むとメモリ不足になる可能性があります。
そのような場面では、InputStream を使い少しずつ読み込みましょう。
書き込みについても同じです。
final var file = Path.of("D:", "java-work", "bbb.data");
System.out.println(file); // D:\java-work\bbb.data
try (final var outputStream = Files.newOutputStream(file)) {
for (int i = 0; i < 10; i++) {
outputStream.write(i * 2);
}
}
// ※バイナリファイルの確認
// --- PowerShell ---
//PS D:\java-work> Format-Hex .\bbb.data
//
// パス: D:\java-work\bbb.data
//
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
//
//00000000 00 02 04 06 08 0A 0C 0E 10 12 ..........
try (final var inputStream = Files.newInputStream(file)) {
System.out.println(inputStream.read()); // 0
System.out.println(inputStream.read()); // 2
System.out.println(inputStream.read()); // 4
// ... 省略 ...
System.out.println(inputStream.read()); // 16
System.out.println(inputStream.read()); // 18
System.out.println(inputStream.read()); // -1
}
OutputStream と InputStream はリソース解放のために必ずcloseしましょう。
できれば try-with-resources文 を使いましょう。
補足
- InputStream でバッファを使って読み込みたい場合は BufferedInputStream を使うことも検討してみましょう。
OutputStream についても BufferedOutputStream が使えます。
空のファイルを作成
Files API | 説明 |
---|---|
Path createFile (Path path, FileAttribute<?>... attrs) | 新しい空のファイルを作成します。 ファイルがすでに存在する場合は失敗します。 |
// --- PowerShell ---
//PS D:\java-work> ls
// <ファイル・ディレクトリなし>
final var file = Path.of("D:", "java-work", "aaa.txt");
System.out.println(file); // D:\java-work\aaa.txt
final var ret = Files.createFile(file);
System.out.println(ret); // D:\java-work\aaa.txt
// --- PowerShell ---
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 0 aaa.txt
// すでに存在しているので失敗(例外が発生)します。
Files.createFile(file); // FileAlreadyExistsException: D:\java-work\aaa.txt
コピー
Files API | 説明 |
---|---|
Path copy (Path source, Path target, CopyOption... options) | sourceからtargetへとファイルをコピーします。 |
コピー元のファイルとして aaa.txt があるとします。
// --- PowerShell ---
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 4 aaa.txt
//
//PS D:\java-work> cat .\aaa.txt
//abcd
final var source = Path.of("D:", "java-work", "aaa.txt");
final var target = Path.of("D:", "java-work", "bbb.txt");
System.out.println(source); // D:\java-work\aaa.txt
System.out.println(target); // D:\java-work\bbb.txt
final var ret = Files.copy(source, target);
System.out.println(ret); // D:\java-work\bbb.txt
// --- PowerShell ---
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 4 aaa.txt
//-a---- 2021/04/23 00:00 4 bbb.txt
//
//PS D:\java-work> cat .\bbb.txt
//abcd
コピー先として bbb.txt ファイルが作成されます。
削除
Files API | 説明 |
---|---|
void delete (Path path) | 対象のファイルまたはディレクトリを削除します。 ディレクトリの場合は空である必要があります。 |
ファイルを削除する例となります。
大事なファイルを削除してしまわないように、十分注意して使いましょう。
// --- PowerShell ---
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 4 aaa.txt
final var file = Path.of("D:", "java-work", "aaa.txt");
System.out.println(file); // D:\java-work\aaa.txt
Files.delete(file);
// --- PowerShell ---
//PS D:\java-work> ls
// <ファイル・ディレクトリなし>
// 存在しないファイルを削除しようとすると失敗(例外が発生)します。
Files.delete(file); // NoSuchFileException: D:\java-work\aaa.txt
移動・名前変更
Files API | 説明 |
---|---|
Path move (Path source, Path target, CopyOption... options) | sourceからtargetへとファイルまたはディレクトリを移動します。 移動元と移動先の親ディレクトリが同じ場合は名前が変更されます。 |
ファイル名変更のコード例です。( aaa.txt → bbb.txt )
// --- PowerShell ---
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 4 aaa.txt
final var source = Path.of("D:", "java-work", "aaa.txt");
final var target = Path.of("D:", "java-work", "bbb.txt");
System.out.println(source); // D:\java-work\aaa.txt
System.out.println(target); // D:\java-work\bbb.txt
// 移動元と移動先の親ディレクトリが同じ場合は名前が変更されます。
final var ret = Files.move(source, target);
System.out.println(ret); // D:\java-work\bbb.txt
//PS D:\java-work> ls
//
// ディレクトリ: D:\java-work
//
//Mode LastWriteTime Length Name
//---- ------------- ------ ----
//-a---- 2021/04/23 00:00 4 bbb.txt
ファイル移動のコード例です。
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//│ aaa.txt
//│
//└─dir
final var source = Path.of("D:", "java-work", "aaa.txt");
final var target = Path.of("D:", "java-work", "dir", "aaa.txt");
System.out.println(source); // D:\java-work\aaa.txt
System.out.println(target); // D:\java-work\dir\aaa.txt
// サブディレクトリへ移動します。
final var ret = Files.move(source, target);
System.out.println(ret); // D:\java-work\dir\aaa.txt
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─dir
// aaa.txt
ディレクトリ
作成
Files API | 説明 |
---|---|
Path createDirectory (Path dir, FileAttribute<?>... attrs) | 新しいディレクトリを作成します。 親ディレクトリが存在しない場合は失敗します。 |
Path createDirectories (Path dir, FileAttribute<?>... attrs) | 存在しない親ディレクトリも含めて作成します。 |
// --- PowerShell ---
//PS D:\java-work> tree /F
// <ファイル・ディレクトリなし>
final var dir = Path.of("D:", "java-work", "dir");
System.out.println(dir); // D:\java-work\dir
final var ret = Files.createDirectory(dir);
System.out.println(ret); // D:\java-work\dir
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─dir
// --- PowerShell ---
//PS D:\java-work> tree /F
// <ファイル・ディレクトリなし>
final var dir = Path.of("D:", "java-work", "aaa-dir", "bbb-dir");
System.out.println(dir); // D:\java-work\aaa-dir\bbb-dir
// 親ディレクトリである"aaa-dir"が存在しないので失敗します。
Files.createDirectory(dir); // NoSuchFileException: D:\java-work\aaa-dir\bbb-dir
// --- PowerShell ---
//PS D:\java-work> tree /F
// <ファイル・ディレクトリなし>
// --- PowerShell ---
//PS D:\java-work> tree /F
// <ファイル・ディレクトリなし>
final var dir = Path.of("D:", "java-work", "aaa-dir", "bbb-dir");
System.out.println(dir); // D:\java-work\aaa-dir\bbb-dir
// 親ディレクトリも含めて作成します。
final var ret = Files.createDirectories(dir);
System.out.println(ret); // D:\java-work\aaa-dir\bbb-dir
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─aaa-dir
// └─bbb-dir
コピー
ディレクトリを丸ごとコピーするAPIは、標準APIでは存在しません。(Java 17 現在)
ファイルのコピー(Files.copy)やディレクトリ作成(Files.createDirectory)、あとは Files.walk などを使って1つずつコピーしていく必要があります。
内容が少し難しくなり基本からはそれるので、本記事では割愛します。
(興味のある方は Files API をご確認ください)
削除
Files API | 説明 |
---|---|
void delete (Path path) | 対象のファイルまたはディレクトリを削除します。 ディレクトリの場合は空である必要があります。 |
ディレクトリの削除はディレクトリが空である必要があります。
ディレクトリの中身も含めて丸ごとで削除する標準APIは存在しません。(Java 17 現在)
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─dir
final var dir = Path.of("D:", "java-work", "dir");
System.out.println(dir); // D:\java-work\dir
Files.delete(dir);
// --- PowerShell ---
//PS D:\java-work> tree /F
// <ファイル・ディレクトリなし>
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─dir
// aaa.txt
// ※ D:\java-work\dirの中にaaa.txtファイルがある状態です。
final var dir = Path.of("D:", "java-work", "dir");
System.out.println(dir); // D:\java-work\dir
// ディレクトリが空ではないので失敗(例外が発生)します。
Files.delete(dir); // DirectoryNotEmptyException: D:\java-work\dir
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─dir
// aaa.txt
補足
- 空ではないディレクトリを削除するには、まずディレクトリ内のファイルをすべて削除する必要があります。
サブディレクトリがあればそのディレクトリ内も削除します。 - ひとつ間違えると、大事なディレクトリがごっそり削除されるので、コーディングする際はご注意ください。
具体的な方法については、別記事の「ディレクトリを丸ごと削除」にまとめています。
移動・名前変更
Files API | 説明 |
---|---|
Path move (Path source, Path target, CopyOption... options) | sourceからtargetへとファイルまたはディレクトリを移動します。 移動元と移動先の親ディレクトリが同じ場合は名前が変更されます。 |
ディレクトリ名変更のコード例です。
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─aaa-dir
// aaa.txt
// bbb.txt
final var source = Path.of("D:", "java-work", "aaa-dir");
final var target = Path.of("D:", "java-work", "bbb-dir");
System.out.println(source); // D:\java-work\aaa-dir
System.out.println(target); // D:\java-work\bbb-dir
// 移動元と移動先の親ディレクトリが同じ場合は名前が変更されます。
final var ret = Files.move(source, target);
System.out.println(ret); // D:\java-work\bbb-dir
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─bbb-dir
// aaa.txt
// bbb.txt
ディレクトリ移動のコード例です。
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//├─aaa-dir
//│ aaa.txt
//│ bbb.txt
//│
//└─bbb-dir
final var source = Path.of("D:", "java-work", "aaa-dir");
final var target = Path.of("D:", "java-work", "bbb-dir", "aaa-dir");
System.out.println(source); // D:\java-work\aaa-dir
System.out.println(target); // D:\java-work\bbb-dir\aaa-dir
// サブディレクトリへディレクトリを移動します。
final var ret = Files.move(source, target);
System.out.println(ret); // D:\java-work\bbb-dir\aaa-dir
// --- PowerShell ---
//PS D:\java-work> tree /F
//
//D:.
//└─bbb-dir
// └─aaa-dir
// aaa.txt
// bbb.txt
補足
- 異なる ファイルストア へ、空ではないディレクトリを移動しようとすると失敗します。
例えば、C:ドライブ、D:ドライブ とあり、C: から D: へディレクトリを移動する場合などです。 - 空のディレクトリやファイルであれば、異なるファイルストアであっても移動できます。
- 詳細は Files.move の API 仕様をご確認ください。
古いAPIはできれば使わないようにする
Java 1からある伝統的なファイル操作のAPIとしてFileクラスがあります。
しかし、Fileクラス にはAPIとしていくつか問題を抱えていました。
その問題を改善するために、Java 7 で新しい API が java.nio.file パッケージに追加されました。
それが、本記事でご紹介してきた Path と Files です。
基本的には古いAPIである Fileクラスは使わないようにすることをおすすめします。
まとめ
本記事では、ファイル操作の基本をご紹介しました。
もう少し詳細も知りたい場合は、関連記事もご参照いただけたら幸いです。