Help us understand the problem. What is going on with this article?

NIO.2でファイルを扱う

More than 3 years have passed since last update.

最近ファイル扱う時はbetter-filesを使っている。
better-files自体はJava NIO周りのScala wrapperなんだけど、そういえばJava NIO.2で追加されたファイル操作周りのAPIをあまり知らなかったから使ってみる。

NIO.2

そもそもNIO.2って?ところで。
Java SE7で導入されたAPIでJava 1.4で導入されたNIOを強化するもの。具体的には以下。

  • ファイルシステムインタフェース
  • 非同期I/O
  • ソケットチャネル機能の完成

NIOに取り込めなかった機能を取り込んでいる。
この後は最初に述べた通りファイル、つまりファイルシステムインタフェースについて見ていきます。

実行環境はこんな感じ。Scalaで書きます。

  • Java: 1.8.0_131
  • Scala: 2.12.3

以下をimportをしていることが前提です。

scala> import java.nio.file._
scala> import java.io.File
scala> import java.nio.file.attribute._

java.nio.file.Path

ファイルやディレクトリの場所を示すインタフェース。
Pathが今回のファイルシステムインタフェースのキモなんじゃないかと思う。
Fileがディレクトリも表すって違和感あったから、パスって考え方ができたのは良い感じ。

インスタンス生成

  • java.nio.file.Pathsを使う
scala> val path1 = Paths.get("dir/file.txt")
path1: java.nio.file.Path = dir/file.txt
  • java.nio.file.FileSystemsを使う
scala> val path2 = FileSystems.getDefault.getPath("dir", "file.txt")
path2: java.nio.file.Path = dir/file.txt
  • java.io.Fileからの変換
scala> val file = new File("dir/file.txt")
file: java.io.File = dir/file.txt

scala> val path3 = file.toPath
path3: java.nio.file.Path = dir/file.txt

絶対パス/相対パス

今までの例は相対パスで書いていた。絶対パスで生成したいなら以下な感じ。

scala> val absolutePath1 = Paths.get("/tmp/dir/file.txt")
absolutePath1: java.nio.file.Path = /tmp/dir/file.txt

scala> val absolutePath2 = Paths.get("/", "tmp", "dir", "file.txt")
absolutePath2: java.nio.file.Path = /tmp/dir/file.txt

java.io.Fileへの変換

scala> val path = Paths.get("dir/file.txt")
path: java.nio.file.Path = dir/file.txt

scala> val file = path.toFile
file: java.io.File = dir/file.txt

java.nio.file.FileSystem

ファイルシステムへのインタフェース。
通常のファイルシステム、ZIPファイル、Jarファイルをこのインタフェースで透過的に扱えるみたい。

java.nio.file.FileSystems

FileSystemを生成するファクトリですね。
Pathを生成する例ではgetDefalutメソッドによりプラットフォームデフォルトのファイルシステムを取得する。

scala> val file = path1.toFile
file: java.io.File = dir/file.txt

java.nio.file.Paths

さっき使ったけどPathを生成するメソッドしかないんで、Pathのファクトリですね。
中でFileSystems.getDefault.getPathを呼んでいるだけなので、こっち使うほうが短くて良いですね。

java.nio.file.Files

ファイルとかディレクトリを扱うインタフェース。
基本的にはPathを渡して、そのPathの実体のファイルとかディクレ取りに対して何かをやる感じ。
commons IOのFileUtilsに似ている。

きっと使う頻度が多いcopy/move/deleteを使ってみる。

copy

scala> val src = Paths.get("dir/src.txt")
src: java.nio.file.Path = dir/src.txt

scala> val dest = Paths.get("dir/dest.txt")
dest: java.nio.file.Path = dir/dest.txt

scala> val path = Files.copy(src, dest)
path: java.nio.file.Path = dir/dest.txt

戻り値は引数で渡したdestをそのまま返します。
srcのファイルが存在しない場合はNoSuchFileExceptionが発生する。
destのファイルが存在している場合はFileAlreadyExistsExceptionが発生する。

copyメソッドには第3引数でCopyOptionというインタフェースを受け取れます。これでどういう感じでコピーするかを決めることができます。
例えばStandardCopyOption.REPLACE_EXISTINGでファイルが存在している場合は置換するって指定ができます。
詳しくは以下を見ると良いです。

move

scala> val src = Paths.get("dir/src.txt")
src: java.nio.file.Path = dir/src.txt

scala> val path = Files.move(src, dest)
path: java.nio.file.Path = dir/dest.txt

ほぼcopyと一緒ですね。

destファイルが存在する場合はFileAlreadyExistsExceptionが発生します。

delete

scala> val path = Paths.get("dir/file.txt")
path: java.nio.file.Path = dir/file.txt

scala> Files.delete(path)

deleteの場合は戻り値がないです。これはPathを削除したら存在しないモノになるから何も返さないんでしょうね。

削除対象のファイルが存在しない場合はNoSuchFileExceptionが発生します。

ここではファイルで試したけど、ディレクトリももちろん扱えます。

PathFilesに渡して操作するAPIはいけている。
他にもFilesからリーダー、ライター、チャネル、ストリームを取得できるメソッドがあって、Fileよりも扱いやすくなっている。

ファイル属性

Fileの場合はOS共通の属性しか扱えなかったけど、NIO.2ではOS毎の属性も扱えるようになっている。
属性とはファイルのオーナー、作成日、更新日、権限とか。

実際に取得してみる。

scala> val path = Paths.get("dir/file.txt")
path: java.nio.file.Path = dir/file.txt

scala> val attributes = Files.getFileAttributeView(path, classOf[PosixFileAttributeView]).readAttributes()
attributes: java.nio.file.attribute.PosixFileAttributes = sun.nio.fs.UnixFileAttributes@364beb26


scala> attributes.creationTime
res0: java.nio.file.attribute.FileTime = 2017-08-28T22:45:16Z

scala> attributes.owner
res1: java.nio.file.attribute.UserPrincipal = asmasa

設定ももちろんできる。

scala> Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis()))
res2: java.nio.file.Path = dir/file.txt

scala> val owner = FileSystems.getDefault.getUserPrincipalLookupService .lookupPrincipalByName("test")
owner: java.nio.file.attribute.UserPrincipal = test

scala> Files.setOwner(path, owner)
res3: java.nio.file.Path = dir/file.txt

こんな感じでもできる。

scala> Files.setAttribute(path, "lastModifiedTime", FileTime.fromMillis(System.currentTimeMillis()))
res4: java.nio.file.Path = dir/file.txt

今更ながらNIO.2のファイルインタフェースを見てみたけど、Fileの時と比べると使いやすくなっていますね。
Filesを通して操作をするっていうのがわかりやすくて良いです。

asmasa
fringe81
Fringeは、最新のテクノロジーとプロフェッショナルによるサービスにより、社会課題に仮説を立てて市場に広げていくことで、数十年という長期的なスパンで価値を生み出し続け、より良い世界を創る集団です。 既存の領域に限らず、時流を読み、仮説を生み出し、テクノロジーの力で優れたサービスを生み出し続けます。
https://www.fringe81.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした