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