【Java】Filesクラスによるファイル操作

2018年3月11日

本稿は、Java1.7より追加されたAPIであるFilesクラスで、Fileクラスよりも高度なファイル操作を行う方法について説明します。

Filesクラスは、基本的にPathクラスを引数にするメソッドを多く含みます。

Pathクラスは、FileクラスのtoPathメソッドや、Pathsクラスのgetメソッドでも生成できます。

また、PathクラスからtoFileメソッドでFileに変換することもできます。

今回はそのFilesクラス関連の機能の一部を説明します。

2017/12/25追記
findメソッド、newDirectoryStreamメソッドの戻り値は使用後にcloseを呼び出したほうが良いメソッドみたいなのでtry-with-resource構文を利用したサンプルに修正しました。

newDirectoryStreamメソッド

指定フォルダの中身のファイルとフォルダをPathの反復子で取得します。

まずは、testフォルダを作成します。

その中に、適当なファイルを入れて試します。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.nio.file.DirectoryStream;
import java.io.IOException;

public class Test{
	public static void main(String[] args)throws IOException{
		Path path = Paths.get("test");
		try( DirectoryStream<Path> ds = Files.newDirectoryStream(path) ){
			for ( Path p : ds ) {
				System.out.println(p);
			}
		} catch( IOException e ) {
			e.printStackTrace();
		}
	}
}
実行結果

test\その他
test\僕.txt
test\画像.jpg
test\私.txt

フォルダの中身を表示することができました。

createDirectoryメソッド

Fileオブジェクトの示す場所にフォルダを作成します。

試しにやっほいフォルダを作成してみます。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.io.IOException;

public class Test{
	public static void main(String[] args)throws IOException{
		Path path = Paths.get("やっほい");
		Files.createDirectory(path);
	}
}
実行結果

やっほいフォルダが作成されましたね。

deleteメソッド

Fileオブジェクトの示す場所のファイルまたはフォルダを削除します。

やっほいフォルダを削除してみます。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.io.IOException;

public class Test{
	public static void main(String[] args)throws IOException{
		Path path = Paths.get("やっほい");
		Files.delete(path);
	}
}
実行結果

やっほいフォルダが削除されましたね。

moveメソッド

ファイルまたはフォルダを移動したり、名前変更したりできます。

テスト.txtファイルをやっほいフォルダに移動してみます。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.io.IOException;

public class Test{
	public static void main(String[] args)throws IOException{
		Path src = Paths.get("テスト.txt");
		Path dest = Paths.get("やっほい/テスト.txt");
		Files.move(src,dest);
	}
}
実行結果

先ほどあった、テスト.txtファイルがなくなりました。

やっほいフォルダの中身

テストファイルが移動されているようです。

copyメソッド

ファイルまたはフォルダをコピーします。

あいえお.txtファイルを作成しました。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Test {
	public static void main(String[] args) throws IOException {
		Path src = Paths.get("あいえお.txt");
		Path dest = Paths.get("あいうえお.txt");
		Files.copy(src, dest);
	}
}

実行結果

あいえお.txtファイルをコピーした、あいうえお.txtファイルが作成されました。

readAllLinesメソッド

簡易的に文字列を読み込むことができます。

test.txtファイルを実行フォルダに作成し、内容を以下のようにして保存しました。

これを簡易的に読み込む方法が次のプログラムです。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class Test {

	public static void main(String[] args) throws IOException {
		Path path = Paths.get("test.txt");
		List<String> lines = Files.readAllLines(path);
		for ( String line : lines ) {
			System.out.println(line);
		}
	}
}
実行結果

あいうえお
かきくけこ

保存ファイルがUTF8でない場合は、readAllLinesメソッドの第二引数の文字コードを保存ファイルの文字コード形式としっかり合わせることで読み込ませることができます。

writeメソッド

簡易的にテキストファイルを作成できます。

test.txtに何かテキストを書き出してみましょう。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class Test {

	public static void main(String[] args) throws IOException {
		Path path = Paths.get("test.txt");
		ArrayList<String> arr = new ArrayList<>();
		arr.add("こんちゃん");
		arr.add("おんちゃん");
		arr.add("あんちゃん");
		Files.write(path, arr);
	}
}
実行結果

findメソッド

指定フォルダからの全階層ファイル、フォルダ検索が可能です。

今回は例として、画像という文字列を含むファイルのみを一覧で検索したいと思います。

流石に、キャプチャを全部乗せると見にくいので省略し、階層を以下のように作成しました。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.function.BiPredicate;
import java.util.stream.Stream;

public class Test{
	public static void main(String[] args)throws IOException{
		Path path = Paths.get("test");

		//検索オブジェクト(画像で、かつファイルの条件)
		BiPredicate<Path, BasicFileAttributes> bp =
				(p,a) -> p.toFile().getName().contains("画像") && a.isRegularFile();

		//引数=(検索フォルダ, 再帰回数, 検索内容)
		try( Stream<Path> stream = Files.find(path, 2 ,bp) ){
			stream
			.map(e->e.toFile().getName())//検索結果をファイル名に変換
			.forEach(System.out::println);//検索結果表示
		} catch ( IOException e ) {
			e.printStackTrace();
		}
	}
}
実行結果

あの画像.jpg
画像1.jpg
画像2.jpg
音楽画像.jpg

画像という文字を含むファイルのみを検索できていますね。

findメソッドの第二引数の数値は再帰回数です。

回数を多くすると、より深いフォルダまで検索していきます。

今回の例ならば、1に指定するとtestフォルダ内のフォルダとファイルのみが検索され、2に指定するともう一階層下の、画像フォルダと音楽フォルダ内部のファイルとフォルダまで検索してくれます。

そして、検索マッチに関する判断は第三引数に指定します。

(p,a)の左の引数がPathオブジェクトで右の引数がBasicFileAttributesオブジェクトになります。この二つの引数を使って検索に引っ掛けるか判定できます。BasicFileAttributesはファイル属性を表すオブジェクトであり、ファイルサイズ取得やフォルダかどうかなどの判断もできます。

今回の場合はp.toFile().getName().contains(“画像”)で画像を含むファイル名であり、かつ!a.isDirectory()でフォルダではないかを判定して検索に引っ掛けています。

walkFileTreeメソッド

指定フォルダからの全階層ファイル、フォルダの削除やコピーが可能です。

先ほど使用した階層を一斉コピーしたいとします。

コピー対象の階層

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

public class Test {

	public static void main(String[] args) throws IOException {
		Path src = Paths.get("test");
		Path dest = Paths.get("テスト");
		Files.walkFileTree(src, new TestFileVisitor(src, dest));
	}
}
class TestFileVisitor implements FileVisitor{

	final Path srcPath;
	final Path destPath;
	
	public TestFileVisitor(Path srcPath, Path destPath) {
		this.srcPath = srcPath;
		this.destPath = destPath;
	}

	@Override
	//中のファイルやフォルダなどを処理する前に呼び出される
	public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
		Path targetdir = destPath.resolve(srcPath.relativize(dir));
		Files.copy(dir,targetdir);
		return FileVisitResult.CONTINUE;
	}

	@Override
	//実行すべきファイルの処理を記述する
	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
		Files.copy(file, destPath.resolve(srcPath.relativize(file)));
		return FileVisitResult.CONTINUE;
	}

	@Override
	//属性読み取り失敗時などに呼び出される
	public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
		return FileVisitResult.CONTINUE;
	}

	@Override
	//フォルダ内のファイルやフォルダなどの処理が全て呼び出された後に呼び出される
	public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
		return FileVisitResult.CONTINUE;
	}
	
}
実行結果

テストフォルダが作成されました。

中身もちゃんとコピーされています。

ここで出てきたFileVisitorインターフェースの実装により少しプログラムが長くなりましたが、内容はそこまで複雑ではありません。

walkFileTreeメソッドを呼び出すと引数に指定したパスにあるフォルダやファイルを順番にすべて確認していき、確認の度に第二引数のFileVisitorの特定のメソッドを呼び出します。

preVisitDirectoryは、そのフォルダの中身の確認をしに行く前に、呼び出されます。

visitFileはファイルが確認された時に呼び出されます。

visitFileFailedはファイルの属性読み取り失敗時などに呼び出されます。

postVisitDirectoryは、そのフォルダの中身のをすべて確認し終わった後に呼び出されます。

さて、コピーをフォルダごと実装する事を考えると、コピー先のフォルダが先に生成されなければなりませんよね?なので、今回はフォルダの中身を確認しに行く前にコピー元のフォルダを作成します。

これができそうなのはpreVisitDirectoryですよね。

そうするとfileVisitでファイル確認する時にはコピー先フォルダは既にできているはずです。なので普通にコピー処理を実装しておきます。

以上でフォルダコピーの簡易実装が完了しました。


せっかくなのでフォルダごと削除する方法も考えてみましょう。

フォルダを削除しようと思うと、中にファイルやフォルダが存在している状態では削除できません。

つまり、先ほどとは逆に中の物を先に削除し、最後にフォルダを削除する必要があります。

すべて処理した後に実行されるメソッドはpostVisitDirectoryでしたね。

ここにフォルダの削除処理を実装すればいいわけです。

それではコピーで作成したテストフォルダごと削除するサンプルをどうぞ。

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

public class Test {

	public static void main(String[] args) throws IOException {
		Path path = Paths.get("テスト");
		Files.walkFileTree(path, new TestFileVisitor());
	}
}
class TestFileVisitor implements FileVisitor{

	@Override
	//フォルダ内のファイルやフォルダなどを処理する前に呼び出される
	public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
		return FileVisitResult.CONTINUE;
	}

	@Override
	//実行すべきファイルの処理を記述する
	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
		Files.delete(file);
		return FileVisitResult.CONTINUE;
	}

	@Override
	//ファイルの処理で属性が読み込めない時などに呼び出される
	public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
		return FileVisitResult.CONTINUE;
	}

	@Override
	//フォルダ内のファイルやフォルダなどの処理が全て呼び出された後に呼び出される
	public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
		Files.delete(dir);
		return FileVisitResult.CONTINUE;
	}
	
}
実行結果

テストフォルダが削除されました。

Java

Posted by nompor