Java

【Java】java.nio.Filesでファイル・フォルダの作成・削除・リネーム・存在チェック・書き込み・読み込み

こんにちは、ともです。半年ぶりに記事を書きます。

Javaでプログラミングをしていると、ファイル・フォルダを作成・削除・リネームなどすることはよくあることだと思います。

それらはJDK1.0からある、java.io.Fileオブジェクトを生成すれば操作可能です。

しかし、Java7から導入されたjava.nio.file.Files、java.nio.file.Path、java.nio.file.Pathsを利用すれば簡潔にかけます。

java.nio.file.Filesを利用して、ファイル・フォルダの扱い方について書きます。

java.nio.file.Filesを確認する

まずは、ドキュメントを見てください。

このドキュメントを見てわかることは、

  1. Filesはstaticファクトリメソッドを提供している
  2. インタフェースには必ずjava.nio.file.Pathをとる

ことが分かると思います。

また

ほとんどの場合、ここで定義されているメソッドは関連付けられたファイル・システム・プロバイダにファイル操作の実行を委譲します。

と書いていることから、Files を利用すればファイルの操作のほとんどが実現できることがわかります。java.io.Fileを直接触らず。

java.nio.file.Pathのインスタンスを生成する

先ほど、java.nio.file.Filesはインタフェースにjava.nio.file.Pathを必要とすることを記載しました。

これは、

  1. 操作対象のファイルへのパス情報をjava.nio.file.Pathが保持する
  2. ファイルへの操作処理自体はjava.nio.file.Filesが保持する

と考えれば理解しやすいと思います。

では、java.nio.file.Pathのインスタンスを生成するにはどうすれば良いのでしょうか。

java.nio.file.Pathインスタンス生成方法は、java.nio.file.Pathsが提供しています。

以下に、ディレクトリを作成する方法です。PathsがPathを生成し、FilesにPathを引き渡す流れです。

String fileName = "testdata/sample.txt";
// PathsがPath生成
Path path = Paths.get(fileName);
// FilesがPathを利用して、ファイル操作
Files.createDirectories(path);

java.nio.file.Filesの流れがわかったので、以降はファイル操作をいくつか紹介します。

ファイル・フォルダの作成・リネーム・削除

  1. ディレクトリを作成
  2. ファイル存在チェック
  3. ファイルがない場合はファイルを作成
  4. ファイルをリネームする
  5. リネーム後のファイルを削除
  6. ディレクトリを削除

上記の流れでコードを書きます。結果、作ったディレクトリもファイルも削除するので何も変わりません。

// ファイルへのパスを書いた文字列
 String fileName = "testdata/sample.txt";
 // Paths to Path
 Path path = Paths.get(fileName);
 // ①testdataフォルダの作成
 Files.createDirectories(path.getParent());
 // ②testdata/sample.txtの存在チェック
 if(Files.notExists(path)) {
     // ③ファイルの作成
     Files.createFile(path);
 }
 // ④作成したファイルをリネーム
 Path renamedPath = Path.of("testdata", "renamed.txt");
 Files.move(path, renamedPath);
 // ⑤リネームしたファイルを削除
 Files.deleteIfExists(renamedPath);
 // ⑥作成したディレクトリを削除
 Files.deleteIfExists(renamedPath.getParent());

PathsがPathオブジェクトを提供すると書きましたが、Paths.of()でも書くことができます。URLのセパレータを気にせずにかけて良いです。

Path.resolve

“testdata”というディレクトリを示すPathが存在したとして、そのパスからの相対パスで色々処理したいんだという場合があると思います。

そのときに以下のようにする必要はないです。

// フォルダ
String folder = "testdata";
StringBuilder sb = new StringBuilder(folder);
// パス1生成(ここだめ)
Path path1 = Paths.get(sb.append("/").append("file1.txt").toString());
// パス2生成(ここだめ)
Path path2 = Paths.get(sb.append("/").append("file2.txt").toString());
// ファイル存在チェック
System.out.println(Files.exists(path1));
System.out.println(Files.exists(path2));

Path.resolveを利用すれば、上記を楽に書けます。

// フォルダ
String folder = "testdata";
Path folderPath = Paths.get(folder);
// パス1生成
Path path1 = folderPath.resolve("file1.txt");
// パス2生成
Path path2 = folderPath.resolve("file2.txt");
// ファイル存在チェック
System.out.println(Files.exists(path1));
System.out.println(Files.exists(path2));

Streamでファイルを一行ずつ読みこむ

newBufferedReaderとかやるの大変ですよね。以下、大変なコード。

File file = new File("testdata/file1.txt");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String str;
while((str = br.readLine())!=null) {
    System.out.println(str);
}
br.close();

大変なので、Streamで以下のように書くと簡潔に書くことができる。ただし、I/O周りは性能に直結するので、StreamよりFileReaderとかを使う方が良いと思います。単体テストとかのデータの読み込みとは以下で良いです。

Path path = Paths.get("testdata/file1.txt");
Files.lines(path).forEach(System.out::println);

BufferedReaderとかWriterとかを生成する

FileReaderやFileWriterからBufferedReaderやWriter作るの大変なので以下のように作成すると楽です。

Path path = Paths.get("testdata/file1.txt");
// ①Files.newBufferedWriterで追記モードで書き込み
BufferedWriter bw = Files.newBufferedWriter(path, StandardOpenOption.APPEND);
bw.write("new line");
bw.newLine();
bw.close();
// ②Files.newBufferedReaderで読み込み
BufferedReader br = Files.newBufferedReader(path);
String str;
while((str = br.readLine()) != null) {
    System.out.println(str);
}
br.close();

FilesからBufferedWriterやReaderを生成して、ファイルの読み書きができました。

まとめ

PathとFilesを利用して、

  1. ファイルの読み書き
  2. ファイル・フォルダの存在チェック
  3. ファイル・フォルダの生成
  4. ファイル・フォルダのリネーム
  5. ファイル・フォルダの削除

を書きました。何か参考になれば幸いです。