68
77

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Java NIO2のおさらいメモ

Last updated at Posted at 2017-08-14

概要

Java 1.7で導入されたNew I/O API (NIO2)をおさらいしたときのメモです。仕事で使いそうな機能を中心に調べたので内容が偏っています。

環境

  • Windows 10 Professional
  • Oracle JDK 1.8.0_144

参考

[Path] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Path.html) / [Paths] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Paths.html)

Pathはインターフェースです。PathのインスタンスはPaths#get、FileSystem#getPath、File#toPathなどで取得できます。
実装クラスはWindows環境(Java 1.8.0_144)ではsun.nio.fs.WindowsPathです。(確認していませんがUnix/Linux環境ではsun.nio.fs.UnixPathと思います。)

Pathのインスタンスの取得

Paths#getを使用すると簡単にPathのインスタンスを取得できます。引数にディレクトリやファイル名を文字列で指定する方法とURIで指定する方法があります。

Path path = Paths.get("C:\\dir\\hoge.txt");

Paths#getは内部でFileSystem#getPathを実行しています。下記のコードは上記のコードと同じ結果になります。

Path path = FileSystems.getDefault().getPath("C:\\dir\\hoge.txt");

ディレクトリ、ファイルを区切って指定することもできます。この場合、ファイルの区切り文字は必要がありません。

Path path = Paths.get("C:", "dir", "hoge.txt");
System.out.println(path.toString());
// → C:\dir1\hoge.txt

Fileとの相互互換

FileからPath

File#toPathを使用します。

File file = new File("hoge.txt");
Path path = file.toPath();

Path から File

Path#toFileを使用します。

Path path = Paths.get("hoge.txt");
File file = path.toFile();

絶対パス、相対パス

実行環境に依存しますがWindows環境ではパスがドライブレターから始まる場合、絶対パスとして解釈されます。
パスが絶対パスかどうかはPath#isAbsoluteで確認できます。

絶対パス

Path path = Paths.get("C:", "dir1", "dir2", "hoge.txt");
System.out.println(path.isAbsolute());
// → true

相対パス

Path path = Paths.get("dir1", "dir2", "hoge.txt");
System.out.println(path.isAbsolute());
// → false

相対パスで表現するディレクトリやファイルを作成する場合user.dirシステムプロパティで指定するディレクトリからの相対位置に作成されます。

下記の例はIDEから実行したときの結果です。(sampleはプロジェクト名です)

String dir = System.getProperty("user.dir");
System.out.println(dir);
// → C:\Users\rubytomato\IdeaProjects\sample

Path file = Paths.get("hoge.txt");
System.out.println(file.toAbsolutePath());
// → C:\Users\rubytomato\IdeaProjects\sample\hoge.txt

冗長なパス表現の解消

相対パスを表現するときに.\\dir1..\\dir2などのようにピリオドを使用することがあります。このようなパス表現を絶対パス化するときにPath#normalizeを使用すると冗長な表現を解消することができます。

Path path = Paths.get("C:\\dir1", "..\\dir2", "hoge.txt");
System.out.println(path.toString());
// → C:\dir1\..\dir2\hoge.txt

Path normalize = path.normalize();
System.out.println(normalize.toString());
// → C:\dir2\hoge.txt

[Files] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html)

Pathで表現するディレクトリ、ファイルの操作を行うクラスです。

ディレクトリの作成

Files#createDirectoryを使用します。

Path dir = Paths.get("C:", "dir1");
Files.createDirectory(dir);

NIOでは

File dir = new File("C:\\dir1");
dir.mkdir();

ファイルの作成

Files#createFileを使用します。

Path file = Paths.get("C:", "dir1", "hoge.txt");
Files.createFile(file);

NIOでは

File file = new File("C:\\dir1\\hoge.txt");
file.createNewFile();

ファイルサイズの取得

Files#sizeを使用します。単位はバイトです。

Path file = Paths.get("C:", "dir1", "hoge.txt");
Long size = Files.size(file);

ネストしたディレクトリを再帰的に作成

ネストしたディレクトリを再帰的に作成したい場合はFiles#createDirectoriesを使用します。

Path path = Paths.get("C:", "dir1", "dir2", "dir3");
Files.createDirectories(path);

なお、上記の例のディレクトリをFiles#createDirectoryで作成しようとするとNoSuchFileExceptionがスローされます。

ディレクトリ、ファイルの存在チェック

Files#exists、Files#nonExistsを使用します。

Path path = Paths("C:", "dir1", "hoge.txt");
if (Files.exists(path)) {
  // ファイルが存在する場合の処理
}
Path path = Paths("C:", "dir1", "fuga.txt");
if (Files.nonExists(path)) {
  // ファイルが存在しない場合の処理
}

存在しないディレクトリ、ファイルの操作

Pathで指定するディレクトリ、ファイルが実際に存在しない場合の実行結果は下記のようになりました。

isXXX系メソッド

// 存在しないファイル
Path nonExistsFile = Paths.get("hoge.txt");

System.out.println(Files.isWritable(nonExistsFile));
// → false

System.out.println(Files.isReadable(nonExistsFile));
// → false

System.out.println(Files.isExecutable(nonExistsFile));
// → false

System.out.println(Files.isDirectory(nonExistsFile));
// → false

isHiddenメソッド

存在しないディレクトリ、ファイルに対して実行するとNoSuchFileExceptionをスローします。

isSameFileメソッド

引数で指定するファイルのどちらか、もしくは両方が存在しない場合はNoSuchFileExceptionをスローします。

deleteメソッド

存在しないディレクトリ、ファイルに対して実行するとNoSuchFileExceptionをスローします。

sizeメソッド

存在しないディレクトリ、ファイルに対して実行するとNoSuchFileExceptionをスローします。

2つのディレクトリ、ファイルのパスの比較

Pathで表現する2つのディレクトリまたはファイルが同じものかを比較するにはFiles#isSameFileを使用します。

Path p1 = Paths.get("C:\\dir1\\dir2\\hoge.txt");
Path p2 = Paths.get("C:\\dir1\\dir3\\..\\dir2\\hoge.txt");
System.out.println(Files.isSameFile(p1, p2));
// → true

上記の例をequalsメソッドで比較するとfalseが返ります。

テンポラリディレクトリの作成

Files#createTempDirectoryを使用します。

指定するディレクトリ内にテンポラリディレクトリを作成する場合

Path root = Paths.get("C:", "temp");
Path tempDir = Files.createTempDirectory(root, "tempDir_");
System.out.println(tempDir.toString());
// → C:\temp\tempDir_1219973832281475457

デフォルトのテンポラリディレクトリ内にテンポラリディレクトリを作成する場合

String dir = System.getProperty("java.io.tmpdir");
System.out.println(dir);
// → C:\Users\rubytomato\AppData\Local\Temp\

Path tempDir = Files.createTempDirectory("tempDir_");
System.out.println(tempDir.toString());
// → C:\Users\rubytomato\AppData\Local\Temp\tempDir_5189589627657871229

デフォルトのテンポラリディレクトリとは

テンポラリディレクトリ・ファイルの作成はTempFileHelperクラスへ委譲されていて、Fileクラスと同様にシステムプロパティjava.io.tmdirで指定されるディレクトリをデフォルトとして使用しています。

[File]
(https://docs.oracle.com/javase/jp/8/docs/api/java/io/File.html)のJavaDoc
デフォルトの一時ファイル・ディレクトリは、システム・プロパティjava.io.tmpdirで指定されます。UNIXシステムの場合、このプロパティのデフォルト値は"/tmp"または"/var/tmp"、Microsoft Windowsシステムの場合は"C:\WINNT\TEMP"です。

テンポラリファイルの作成

Files#createTempFileを使用します。

指定するディレクトリ内にテンポラリファイルを作成する場合

Path root = Paths.get("C:", "temp");
Path tmpFile = Files.createTempFile(root, "temp_", ".txt");
System.out.println(tmpFile.toAbsolutePath().toString());
// → C:\temp\temp_4672863899169116557.txt

デフォルトのテンポラリディレクトリ内にテンポラリファイルを作成する場合

Path tmpFile = Files.createTempFile("temp_", ".txt");
System.out.println(tmpFile.toAbsolutePath().toString());
// → C:\Users\rubytomato\AppData\Local\Temp\temp_2673655805075198029.txt

NIOでは

File tmpFile = File.createTempFile("temp_", ".txt", "C:\\temp");

ディレクトリ、ファイルの探索

サンプル

C:\dir1
    |
    +--- \dir2
    |      |
    |      +--- \dir4
    |      |      |
    |      |      +--- file4.csv
    |      |      +--- file4.dat
    |      |
    |      +--- \dir5
    |      |      |
    |      |      +--- file5.csv
    |      |      +--- file5.txt
    |      |
    |      +--- file2.txt
    |
    +--- \dir3
    |      |
    |      +--- \dir6
    |      |      |
    |      |      +--- file6.txt
    |      |
    |      +--- \dir7
    |      |
    |      +--- file3.csv
    |
    +--- file1.txt

Files#listを使った探索

引数で指定したディレクトリ下のディレクトリ、ファイルを探索します。サブディレクトリ以下の再帰的な探索は行いません。

Path startDir = Paths.get("C:", "dir1");
Files.list(startDir).forEach(System.out::println);
// → C:\dir1\dir2
// → C:\dir1\dir3
// → C:\dir1\file1.txt

Files#walkを使った探索

第1引数で指定したディレクトリ下を第2引数(optional)で指定した階層まで再帰的に探索します。
下記の例ではdir1ディレクトリから第2階層までのディレクトリ、ファイルを探索します。

Path startDir = Paths.get("C:", "dir1");
Files.walk(startDir, 2).forEach(System.out::println);
// → C:\dir1
// → C:\dir1\dir2
// → C:\dir1\dir2\dir4
// → C:\dir1\dir2\dir5
// → C:\dir1\dir2\file2.txt
// → C:\dir1\dir3
// → C:\dir1\dir3\dir6
// → C:\dir1\dir3\dir7
// → C:\dir1\dir3\file3.csv
// → C:\dir1\file1.txt

Files#findを使った探索

第3引数で指定する条件に一致するディレクトリ、ファイルを再帰的に探索します。
下記の例ではdir1ディレクトリから第3階層までの拡張子がcsvのファイルを探索します。

Path startDir = Paths.get("C:", "dir1");

BiPredicate<Path, BasicFileAttributes> matcher = (path, attr) -> {
    if (attr.isRegularFile() && path.getFileName().toString().endsWith(".csv")) {
        return true;
    }
    return false;
};

Files.find(startDir, 3, matcher).forEach(System.out::println);
// → C:\dir1\dir2\dir4\file4.csv
// → C:\dir1\dir2\dir5\file5.csv
// → C:\dir1\dir3\file3.csv

PathMatcherを使った探索

PathMathcerを使うと正規表現に似た記法(glob)で検索条件を記述することができます。
下記の例ではdir4ディレクトリ下の拡張子がcsvのファイルを探索します。

FileSystem fs = FileSystems.getDefault();
PathMatcher matcher = fs.getPathMatcher("glob:**/dir4/*.csv");

Path startDir = Paths.get("C:", "dir1");
Files.walk(startDir).filter(matcher::matches).forEach(System.out::println);
// → C:\dir1\dir2\dir4\file4.csv

ファイルのコピー

a.txtをb.txtへコピーします。

Path source = Paths.get("path", "to", "a.txt");
Path target = Paths.get("path", "to", "b.txt");

Files.copy(srouce, target);

コピー先のb.txtファイルが存在している場合はFileAlreadyExistsExceptionがスローされます。

ファイルのリプレイス

コピー先のファイルをリプレイスする場合は第3引数のコピーオプションにStandardCopyOption.REPLACE_EXISTINGを指定します。

Path source = Paths.get("path", "to", "a.txt");
Path target = Paths.get("path", "to", "b.txt");

Files.copy(srouce, target, StandardCopyOption.REPLACE_EXISTING);

指定するディレクトリへコピーする

path/to/a.txtをpath/to/other/a.txtへコピーします。

Path source = Paths.get("path", "to", "a.txt");
Path targetDir = Paths.get("path", "to", "other");

Files.copy(srouce, targetDir.resolve(source.getFileName()));

[FileSystem] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/FileSystem.html) / [FileSystems] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/FileSystems.html)

FileSystemは抽象クラスです。下記に引用したJavaDocの説明の通り実行環境のファイルシステムを操作するための機能を提供します。
実装クラスはWindows環境(Java 1.8.0_144)では、sun.nio.fs.WindowsFileSystemです。確認していませんが、その他の実装クラスとしてはsun.nio.fs.LinuxFileSystem sun.nio.fs.SolarisFileSystemなどがあるようです。

ファイル・システムへのインタフェースを提供し、ファイル・システム内のファイルやその他のオブジェクトにアクセスするためのオブジェクトのファクトリです。

FileSystemのインスタンスはFileSystems#getDefaultで取得します。

FileSystem fs = FileSystems.getDefault();
System.out.println(fs.getClass().getCanonicalName());
// → sun.nio.fs.WindowsFileSystem

ZipFileSystem

FileSystemの実装クラスにzipファイルをファイルシステムとして扱うZipFileSystemというクラスがあります。

Path path = Paths.get("zip_demo.zip");

try (FileSystem zip = FileSystems.newFileSystem(path)) {
  System.out.println(zip.getClass().getCanonicalName());  
  // → jdk.nio.zipfs.ZipFileSystem

  Path root = zip.getPath("/");
  Files.walk(root).forEach(System.out::println);
}

Zipファイルシステムのプロパティー

Map<String, String> env = Map.of("create", "false",
                                 "encoding", "UTF-8");

FileSystems.newFileSystem(path, env);

ルートディレクトリ

実行環境のルートディレクトリはFileSystem#getRootDirectoriesで確認することができます。
下記の例の実行結果は私のPCのものになります。(光学ドライブもルートディレクトリとして識別されています。)

FileSystem fs = FileSystems.getDefault();
fs.getRootDirectories().forEach(System.out::println);
// → C:\
// → D:\
// → E:\

ファイル区切り文字

FileSystem#getSeparatorを使用します。

FileSystem fs = FileSystems.getDefault();
System.out.println(fs.getSeparator());
// → \

[FileSystemProvider] (https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/spi/FileSystemProvider.html)

FileSystemProviderは抽象クラスです。下記に引用するJavaDocの説明の通り、Filesクラスで行う操作はFileSystemProviderへ委譲されています。
実装クラスはWindows環境(Java 1.8.0_144)では、sun.nio.fs.WindowsFileSystemProviderです。

ファイル・システムのサービス・プロバイダ・クラスです。Filesクラスによって定義されているメソッドは通常、このクラスのインスタンスに処理を委譲します。

FileSystem fs = FileSystems.getDefault();
FileSystemProvider provider = fs.provider();
System.out.println(provider.getClass().getCanonicalName());
// → sun.nio.fs.WindowsFileSystemProvider

補足

Options

In memory File System

一時ファイルの削除

その他のおさらいメモ

68
77
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
77

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?