Java : ファイル操作の基本

ファイルの読み込み、書き込み、削除、コピー…ディレクトリの作成・削除などなど。
ファイル操作の基本をコード例つきでご紹介します。

注意

  • 本記事のコード例はWindows10で実行しています。

基本

ファイル操作の基本は、

となります。

基本シーケンス

リソース解放は忘れずに

基本といいつつ少し難しめの内容で申し訳ないのですが、とても重要なので説明します。

ファイルを操作する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) ファイルから文字列を読み込みます。

基本的には writeStringreadString がおすすめです。
リソース解放をする必要がありません。

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) ファイルからバイトデータを読み込みます。

基本的には writereadAllBytes がおすすめです。
リソース解放をする必要がありません。

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.txtbbb.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 パッケージに追加されました。
それが、本記事でご紹介してきた PathFiles です。

基本的には古いAPIである Fileクラスは使わないようにすることをおすすめします。

関連記事:FileクラスよりもPathとFilesを使おう

まとめ

本記事では、ファイル操作の基本をご紹介しました。
もう少し詳細も知りたい場合は、関連記事もご参照いただけたら幸いです。


関連記事

ページの先頭へ