倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

これからの「Java I/O」の話をしようwww (4) : Files クラスのメソッド 〜ファイル、ディレクトリの操作〜

Java nio の API を見ていくシリーズ(目次)。 前回から結構時間が空いてしまいましたが、java.nio.file パッケージのクラスを見ていきま〜す。 今回はファイル、ディレクトリの操作です。 ファイルをコピーとか移動とかします。 以降の記事では「ファイル」と書いてファイルとディレクトリの両方を指している場合があります。 文脈で分かるかと思いますが。

ファイルの存在と削除

まずはファイルの存在を確認するメソッドと、ファイルを削除するメソッド。

メソッド 返り値 説明
exists(Path, LinkOption...)
notExists(Path, LinkOption...)
boolean ファイル、ディレクトリが存在するかどうかを確認する
delete(Path)
deleteIfExists(Path)
void
boolean
ファイル、ディレクトを削除する。
  • ファイルにアクセス不可能な場合など、exists() メソッドと notExists() メソッドがどちらも false を返すことがあります
  • delete() メソッドは、引数のファイル(ディレクトリ)が存在しなければ NoSuchFileException を投げます
  • deleteIfExists() メソッドは、引数のファイル(ディレクトリ)が存在すれば削除し true を返します。 存在しなければ false を返します
  • delete() メソッド、deleteIfExists() メソッドでディレクトリを削除しようとすると、ディレクトリが空なら削除します。 空でないなら DirectoryNotEmptyException を投げます
  • delete() メソッド、deleteIfExists() メソッドでシンボリック・リンクを削除しようとすると、シンボリック・リンク自身を削除します。 リンク先のファイルは削除されません

簡単なサンプル・コード(Groovy):

// 「C:/sample/code/of/java/nio/file/」フォルダにて実行
import java.nio.file.*

Files.deleteIfExists(Paths.get("source.java"))

def path = Paths.get("C:/sample/code/of/java/nio/file/source.java")
def file = Files.createFile(path)    // サンプルのためにファイルを作る(成功したとしましょう)

// ファイルが存在するかどうか確認
assert Files.exists(Paths.get("source.java"))    // さっき作ったので存在する
assert Files.notExists(Paths.get("dest.class"))    // 作ってないファイルなので存在しない

// ファイルの削除
assert Files.delete(Paths.get("source.java")) == null    // 作ったファイルを削除
assert !Files.deleteIfExists(Paths.get("source.java"))    // ファイルが存在するなら削除。 今は存在しないので false を返す

上記の exist(), notExists() メソッドに渡せる LinkOption クラスは列挙型 (enum) として java.nio.file パッケージに定義されています:

package java.nio.file;

public enum LinkOption
        extends Enum<LinkOption>
        implements OpenOption, CopyOption{
    NOFOLLOW_LINKS
}

定数は1つだけ定義されてます:

定数 説明
NOFOLLOW_LINKS シンボリック・リンクはリンク先をフォローしない

LinkOption 定数は CopyOption インターフェース、OpenOption インターフェースを実装しています。 CopyOption インターフェースには後ほど、OpenOption インターフェースは後日もう少し詳しく見ます。 Option 関連のインターフェース、クラスの関係は数のようになっています:

ファイルのコピーと移動

次はファイルのコピーと移動を行うメソッド。

メソッド 返り値 説明
copy(Path, Path, CopyOption...)
copy(InputStream, Path, CopyOption...)
copy(Path, OutputStream)
Path
long
long
ファイルをコピーする。
move(Path, Path, CopyOption...) Path ファイルを移動する。
  • CopyOption は後ほど。
  • copy() メソッドによってディレクトリをコピーしようとすると、空のディレクトリが作成されます。 ディレクトリ内のファイルはコピーされません
  • copy() メソッドによってシンボリック・リンクをコピーしようとすると、シンボリック・リンクがサポートされているなら、デフォルトではリンク先のファイルがコピーされます。 CopyOption として NOFOLLOW_LINKS が指定されていると、シンボリック・リンク自身がコピーされます
  • copy() メソッドには、Path から Path へコピーするものの他に、InputStream から読み込んで Path に書き出すもの、Path から読み込んで OutputStream へ書き出すものがあります*1。 CopyOption を指定できるのは Path へ書き出す場合だけです
  • move() メソッドによってディレクトリを移動しようとすると、空のディレクトリでないなら移動を行います。 ディレクトリが空でないなら DirectoryNotEmptyException を投げます*2
  • move() メソッドはファイル・ディレクトリの名前の変更にも使用できます。 名前変更は空でないディレクトリに対しても行えます
  • move() メソッドによってシンボリック・リンクを移動しようとすると、シンボリック・リンク自身が移動されます move() メソッドではNOFOLLOW_LINKS オプションはサポートされていないので注意

ではサンプル・コード。 ここでは Path から Path へコピーするサンプルだけ:

// 「C:/sample/code/of/java/nio/file/」フォルダにて実行
import java.nio.file.*

// 準備
Files.deleteIfExists(Paths.get("source.java"))

def from = Paths.get("C:/sample/code/of/java/nio/file/source.java")    // コピー元
Files.createFile(from)

def to = Paths.get("C:/sample/code/source.java")    // コピー先

// ファイルのコピー
Files.deleteIfExists(to)    // コピー先にファイルがあると例外が投げられるので、あるなら削除
Files.copy(from, to)    // コピー
assert Files.exists(from) && Files.exists(to)    // コピー元もコピー先もファイルは残っている

from = to
to = Paths.get("C:/sample/code/of/java/nio/file/dest.java");

// ファイルの移動
Files.move(from, to)    // 移動
assert Files.notExists(from) && Files.exists(to)    // 移動元のファイルは削除、移動先のファイルはある

コピーや移動は一行で終わりますが、サンプルではファイルが存在するかどうかのアサーションをあれこれ行ってます。

CopyOption

copy() メソッドや move() メソッドには CopyOption を指定することができます。 CopyOption は LinkOption (列挙型)と違ってインターフェースです。 ちょっと紛らわしいですが。 CopyOption インターフェースを実装しているクラスは

  • StandardCopyOption
  • LinkOption

です。 LinkOption は先ほど見ました。 以下では CopyOption インターフェースと StandardCopyOption 定数 (enum) をそれぞれ見ていきましょう。

CopyOption インターフェース
CopyOption インターフェースは特にメソッドは定義されていません。

package java.nio.file;

public interface CopyOption{}

StandardCopyOption 定数
StandardCopyOption のクラス定義は以下のようになっています:

package java.nio.file;

public enum StandardCopyOption 
        extends Enum<StandardCopyOption>
        implements CopyOption{
    ATOMIC_MOVE,
    COPY_ATTRIBUTES,
    REPLACE_EXISTING
}

定義されている定数は以下の通り:

定数 説明
ATOMIC_MOVE アトミックなファイルシステム操作として実行する
COPY_ATTRIBUTES 属性もコピーする
REPLACE_EXISTING コピー先に既にファイルがある場合、それを新しいファイルで置き換える

これらのうち、REPLACE_EXISTING を使ったサンプルを書いてみます。 内容は先ほどのコピーのサンプルと同じ:

// 「C:/sample/code/of/java/nio/file/」フォルダにて実行
import java.nio.file.*;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

// 準備
Files.deleteIfExists(Paths.get("source.java"))

def from = Paths.get("C:/sample/code/of/java/nio/file/source.java")    // コピー元
Files.createFile(from)

def to = Paths.get("C:/sample/code/source.java")    // コピー先

// コピー
Files.copy(from, to, REPLACE_EXISTING)    // コピー先のファイルが存在しても上書きでコピーを実行
assert Files.exists(from) && Files.exists(to)

まぁ、使い方は簡単ですね。 ただし、コピーと移動でサポートしている CopyOption が異なるようなので注意

メソッド copy() move()
NOFOLLOW_LINKS ×
ATOMIC_MOVE ×
COPY_ATTRIBUTES ×
REPLACE_EXISTING

詳しくは copy(), move() の JavaDoc などを参照。

シンボリック・リンクを読み込む

最後はシンボリック・リンクを読み込むメソッド。

メソッド 返り値 説明
readSymbolicLink(Path) Path シンボリック・リンクを読み込む
  • 引数の Path オブジェクトがシンボリック・リンクなら、そのリンク先を Path オブジェクトとして返します
  • リンク先のファイルが存在しなくても OK
  • 引数の Path オブジェクトがシンボリック・リンクでなければ NotLinkException が投げられます

プログラミングGROOVY

プログラミングGROOVY

*1:返り値の long は読み書きしたバイト数。

*2:JavaDoc には書かれてますが、実際に試したら中のファイルも移動されてたんだが・・・