【Java】10分以内でわかるファイル操作

# はじめに
皆さん、java.io.Fileクラスとjava.nio.file.Filesクラスをご存知でしょうか。この２つを理解する事によって、ファイル操作をすることができるようになります。それでは詳しく見ていきましょう。

# Fileクラスとは？
java.io.Fileはフォルダー／ファイルの情報を管理すると共に、フォルダー／ファイルを操作するための手段を提供するクラスです。具体的にPathを扱う方法はFileクラスのコンストラクタにファイル名やディレクトリ名を渡して、インスタンスを生成するとカレントディレクトリーにあるパスを扱うことができます。

```
File file = new File("x.text");
```
# 使用方法

## ファイルの存在を確かめ、存在しなければ新しいファイルを作成する方法
まずファイルの存在を確かめる為には**exesitsメソッド**を使用する必要があります。exsitsメソッドは目的のファイルが存在すればtrue、存在しなければ、falseを返します。
そして、新しいファイルを作成する為には**createNewFileメソッド**を使用する必要があります。サンプルコードを通して、具体的に見ていきましょう。

```Sample.java
package innerclass;

import java.io.File;
import java.io.IOException;

public class Sample {
	public static void main(String[] args) throws IOException{
		//「x.text」を扱えるようにする
		File file = new File("x.java");

		//existメソッドでファイルの存在を確かめ、存在がなければ新しいファイルを作成する
		if(file.exists() == false) {
			file.createNewFile();
		}
		//作成されたファイルの絶対パスを確認
		System.out.println(file.getAbsolutePath());
	}
}
```
下記のパスに「x.txt」が作成されました。
<p align="center">
<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/8cdec872-ee2b-5c39-692e-6113b871f7d9.png"/>
</p>

```
.../Eclipse_2020-12.app/Contents/workspace/innerclass/x.text
```
### 新規ディレクトリーを作成するの場合
**mkdirsメソッド**を使用しましょう。先ほどのサンプルコードのcreateFileNewメソッドの部分をmkdirsメソッドに変更して実行してみます。

```
		File dir = new File("new");

		if(dir.exists() == false) {
			dir.mkdir();
		}
```

下記のバスにnewディレクトリーが作成されました。


<p align="center">
<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/3a6f77bc-b019-0195-4a6d-2a1cfe99424d.png"/>
</p>

```
.../Eclipse_2020-12.app/Contents/workspace/innerclass/new
```

## リストやディレクトリーの一覧を取得する場合
listFilesメソッドを使用します。FileクラスのlistFilesメソッドは、指定したディレクトリに含まれるファイルやディレクトリを配列として返します。

```Sample.java
package innerclass;

import java.io.File;
import java.io.IOException;

public class Sample {
	public static void main(String[] args) throws IOException{
		//カレントディレクトリーへのパスを表すFileのインスタンスを作成
		File dirList= new File(".");
		show(dirList, "");
}
	private static void show(File dir, String indent) {
		//ファイルやディレクトリーの一覧を取得して変数filesに格納
		File[] files = dir.listFiles();
		//getNameメソッドを使用して、ファイル名やディレクトリー名をコンソールに表示
		for (File file : files) {
			System.out.println(indent + file.getName());
			//ディレクトリーであれば、showTreeメソッドを再帰する
			if(file.isDirectory()) {
				show(file, indent + " ");
			}
		}
	}
}

```

```
bin
x.text
.classpath
.settings
.project
new
src
 innerclass
  SampleCollector.java
  SampleThrad.java
  Sample.java
  Man.java
```

## NIO.2とは？
java.io.Fileクラスは設計思想が古く、ディレクトリーツリーの探索が手軽できないことや、ファイルのコピーが簡単にできないなど課題がありました。それを解決するためにJavaSE7で導入されたのが**NIO.2**です。特徴はファイルやディレクトリーのパスを表す機能とファイル操作の機能が完全に分離さてています。ファイルやディレクトリーのパスを表すには**java.nio.file.Pathインターフェース**を使用し、ファイルを操作するには**java.nio.file.Filesクラス**を使用します。

# 使用方法

## ファイルやディレクトリーのパスを扱う方法
パスのインスタンスへの参照はPathを利用しやすくするためのjava.nio.file.Pathsクラスの**getメソッド**を使用します。引数に目的のファイルやディレクトリへのパスを記述します。



```
Path path = Paths("dir/sample.text");
```
## 新しいファイルの作成
ファイルの作成はFilesクラスのcreateFileメソッドを使用します。
```sample.java
package innerclass;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class Sample {
	public static void main(String[] args) throws Exception{
		System.out.print(">");
		//Scannerクラスのインスタンスを作成し、キーボードから入力された文字列や数値を受け取って、処理する
		Scanner scan = new Scanner(System.in);
		String name = scan.nextLine();
		//dirディレクトリ配下のファイル(コンソールで指定)のパスを取得
		Path p = Paths.get("dir", name);

		//もし指定したファイルが存在しなければ、新しくファイルを作成
		if(Files.exists(p) == false) {
			Files.createFile(p);
		}
		//最終更新日をコンソールに出力
		System.out.println(Files.getLastModifiedTime(p));
		//ファイルのアクセス権を調べるためのメソッド
		System.out.println(Files.getPosixFilePermissions(p));
	}
}
```
コンソールにaを入力しているのでaのファイルが作成されました。
```
>a
2022-04-04T11:50:59.240684Z
[OTHERS_READ, OWNER_READ, OWNER_WRITE, GROUP_READ]
```
<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/12707624-c74c-a995-ab4c-d8540ca11c21.png"/>

## ディレクトリーを作成する場合
createDirectoriesメソッドを使います。ファイル作成した時のサンプルコードのcreateFileの部分をcreateDirectoriesに置き換えてみます。
```
Files.createDirectory(p);
```
コンソールにbを入力しているのでbのディレクトリーが作成されました。
```
>b
2022-04-04T12:26:24.635773Z
[GROUP_READ, OWNER_READ, OTHERS_EXECUTE, OWNER_WRITE, OTHERS_READ, OWNER_EXECUTE, GROUP_EXECUTE]
```

<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/ad7f38dd-650b-0111-f284-a2e72baa08e5.png"/>

### 親ディレクトリが存在しない時でもディレクトリを作成したい場合
**createDirectoriesメソッド**を使います

```
FIle.createDirectories(p);
```
## 既存のディレクトリー内のパスを表現する場合
Pathsクラスの**getメソッド**を使用します。getメソッドは引数は２つの引数を受け取ります。1つ目は単一の文字列。２つ目は文字列の可変長引数です。可変長引数なので、いくつでも引数を渡す他に、引数なしでも可能です。
```
Path get(String first, String... more)
```
## パス情報を結合したい場合
同じディレクトー内にファイルを複数作る時、コードが乱雑になってしまいます。例えば、ファイルを２つ作成したい場合は2つとも同じパスを書かないといけません。そんな時Pathsクラスの**resolveメソッド**を使用します。Path オブジェクトが持つパスの情報に別の Path オブジェクトが持つパス情報を結合した新しい Path オブジェクトを返します。

```
Path dir = Paths.get("dir","subdir");
Path file = dir.resolve(Paths.get("a.txt"));
```

## ファイルをコピーしたい場合
Filesクラスのcopyメソッドを使用します。第1引数にコピー元へのパス、第２引数にコピー先へのパスを指定します。

```Sample.java
package innerclass;

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

public class Sample {
	public static void main(String[] args) throws Exception{
		//既存のディレクトリ内のパスを指定
		Path sample = Paths .get ("dir", "sample.txt");
		//指定したパスにファイルがなければ新しく作成する
		if(Files.exists(sample)== false) {
			Files.createFile(sample);
		}
		Path outFile = Paths.get("dir","sample_bk.txt");
		//sample.txtをコピーする
		Files.copy(sample,outFile);
	}
}
```
その結果、sample.txtがコピーされてsample_bk.txtが作成されました。

<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/e7d912c9-9c38-ae4a-abff-70c6643cc466.png"/>

## ファイルを移動したい場合
Filesクラスのmoveメソッドを使用します。第1引数に移動元へのパス、第２引数に移動先へのパスを指定します。先ほどのコードに下記のコードを追加してみます。

```
	Path dir = Paths.get("dir","b");
	Path dest = dir.resolve(Paths.get("sample.txt"));
	Files.move(sample,dest);
```
実行後、bフォルダーにsample.textが移動されました。
<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/242850db-1a16-0269-5bb9-b8e61165b0e7.png"/>

## ファイル内のデータを読み込みたい場合
Filesクラスの**newBufferdReaderメソッド**を使用します。
```
Path sample = Paths.get("sample.txt");
BufferedReader br = Files.newBufferedReader(sample);
try(br){
   br.lines().forEach(System.out::println);
}
```

## ファイル内のデータを書き込みたい場合
Filesクラスの**newBufferedWriterメソッド**を使用します。

```
Path sample = Paths.get("sample.text);
BufferdWriter out = Files.newBufferedWriter(sample);
try(out){
   out.write("こんにちは");
   out.newLine();
}
```

### 追記で書き込む例
newBufferedWriterメソッドの引数に、java.nio.file.OpenOptionインターフェース型のAPPENDを指定します。

```sample.java
package innerclass;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;

public class Sample {
	public static void main(String[] args) throws Exception{
		Path sample = Paths.get ("dir","sample.txt");
		if(Files.exists(sample)== false) {
			Files.createFile(sample);
		}
		//標準入力を取得する
		Scanner scan = new Scanner(System.in);
		//引数にtandardOpenOption.APPENDを指定すると、追記できる
		BufferedWriter out = Files.newBufferedWriter(sample,StandardOpenOption.APPEND);
		try(scan; out){
			System.out.print("> ");
			out.write(scan.nextLine());
			out.newLine();
		}

		BufferedReader in = Files.newBufferedReader(sample);
		try(in){
			in.lines().forEach(System.out::println);
		}
	}
}
```
aaaaaaを追記してみます。
そうすると、sample.textに追記されていることがわかります。
```
> aaaaaa

aaaaaa
```

<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/6a068884-f88d-4c75-3684-81283e643518.png"/>

### ディレクトリー内のすべてのパスを取得したい場合
Filesクラスの**listメソッド**を使用します。引数で渡されたディレクトリ内のディレクトリやファイルを表すPathオブジェクトの一覧をStreamオブジェクトとして戻します。

```
Files.list(base).forEach(System.out::println);
```
### ディレクトリー内を再起的に処理する場合
Filesクラスの**walk**メソッドを使用します。
```
Files
  .walk(base.resolve("src"))
  .forEach(System.out::println);
```

### ディレクトリの再帰的な探索を行いたい場合
**walkFileTreeメソッド**を使用します。4つのタイミングで処理を実行し、それに合わせて4つの抽象メソッドが定義されている。
![スクリーンショット 2022-04-20 7.48.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/d7290df2-daac-28d7-5001-8061630e42a5.png)

```Sample.java
package innerclass;


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

public class Sample {
	public static void main(String[] args) throws Exception{
		Path dir = Paths.get("dir","data");
		if(Files.exists(dir) == false) {
			Files.createDirectories(dir);
		}
		//resolceメソッド：パス情報にパス情報を統合する
		Files.createFile(dir.resolve(Paths.get("a.text")));
		Files.createFile(dir.resolve(Paths.get("b.text")));
		Files.createFile(dir.resolve(Paths.get("c.text")));

		Files.list(dir).forEach(System.out::println);

		//dirディレクトリ内を再起的に移動しながら、該当するファイルを削除していく
		Files .walkFileTree(dir,new SimpleFileVisitor<Path>() {
			/*
			 * 本来であれば４つの抽象メソッドが必要だが、FileVisitResultクラスをオーバーライド
			 * する事によって必要がなくなる。
			 */
			@Override
			public FileVisitResult visitFile(
					Path file,
					BasicFileAttributes attrs) throws IOException{
				Files.delete(file);
				//ファイルを探索を続けるためにCONTINUEを返えす
				return FileVisitResult.CONTINUE;
			}
		});
		Files.delete(dir);
		System.out.println("deleted");
		}
	}
```
```
dir/data/a.text
dir/data/c.text
dir/data/b.text
deleted
```
# まとめ
#### java.io.Fileクラス
<img width="700" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/66333d63-44a6-14ec-e88d-196c72b7518f.png"/>

#### java.nio.file.Filesクラス
<img width="700" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526916/9d92053b-469d-773b-e91c-badd18d1fadc.png"/>