倭マン's BLOG

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

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

Java nio の API を見ていくシリーズ(目次)。 今回はファイル、ディレクトリのプロパティや属性を取得・設定するメソッドを見ていきます。 ただし、メソッド名に “Attribute” がつくメソッド(readAttribute(), setAttribute() など)は次回に。 今回見ていくのは

です。

Files クラスの宣言

今回見ていく Files クラスのメソッドは以下のものです:

package java.nio.file;

public final Class Files{
    ...

    // isXxxx() メソッド
    public static boolean isRegularFile(Path path, LinkOption... options)
    public static boolean isDirectory(Path path, LinkOption... options)
    public static boolean isSymbolicLink(Path path)
    public static boolean isExecutable(Path path)
    public static boolean isHidden(Path path)
    public static boolean isSameFile(Path path, Path path2)

    // ファイル・サイズ
    public static long size(Path path)

    // 最終更新時刻
    public static FileTime getLastModifiedTime(Path path, LinkOption... options)
    public static Path setLastModifiedTime(Path path, FileTime time)

    // ファイル所有者
    public static UserPrincipal getOwner(Path path, LinkOption... options)
    public static Path setOwner(Path path, UserPrincipal owner)

    // POSIX ファイル権限
    public static
    Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options)

    public static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms)
}
  • isXxxx() メソッドの中で、前回見た isReadable(), isWritable() メソッドは飛ばしてます。

isXxxx() メソッド

メソッド 返り値 説明
isRegularFile(Path, LinkOption...) boolean 引数の Path が通常のファイルかどうかをテスト
isDirectory(Path, LinkOption...) boolean 引数の Path がディレクトリかどうかをテスト
isSymbolicLink(Path) boolean 引数の Path がシンボリック・リンクかどうかをテスト
isExecutable(Path) boolean 引数の Path が実行可能かどうかをテスト
ファイルが存在するかチェック
JVM が実行できる権限を持っているかもチェックする
isHidden(Path) boolean 引数の Path が隠しファイルかどうかをテスト
アルゴリズムファイルシステムに依存する*1
isSameFile(Path, Path) boolean 2つの引数の Path が同じものかどうかをテスト
ファイルが存在するかどうかはチェックされない

ではいくつかの isXxxx() メソッドを使うサンプル・コード。 前回は Groovy で try-with-resources 文がサポートされてない*2ので Java で書きましたが、今回はまた Groovy で書いてます:

import java.nio.file.*
import java.nio.charset.Charset

// 準備
def dir = Paths.get("dir")
Files.createDirectories(dir)

def src = dir.resolve("source.java")
Files.deleteIfExists(src)
Files.createFile(src)
Files.write(src, ["first line", "second line", "third line"], Charset.defaultCharset())

// isRegularFile(), isDirectory() メソッド
assert Files.isRegularFile(src) && !Files.isDirectory(src)
assert !Files.isRegularFile(dir) && Files.isDirectory(dir)

// isExecutable() 実行可能?
// ${JAVA_HOME}/bin/javac.exe (exe ファイル)
def javac = Paths.get(System.getenv("JAVA_HOME")).resolve("bin/javac.exe")
assert Files.isExecutable(javac)

// isSameFile() 同じファイルかどうかテスト
def path = Paths.get("C:/sample/code/of/java/nio/file/dir/source.java")
assert Files.isSameFile(src, path)

ファイル・サイズ

ファイル・サイズを取得するメソッド。 返り値は long 値です。

メソッド 返り値 説明
size(Path) long ファイル・サイズを取得する

サンプル・コード・・・あんまり意味ないかな?:

import java.nio.file.Files

// 準備
def src = ...

println Files.size(src)

最終更新時刻

最終更新日時を取得・設定するメソッド

メソッド 返り値 説明
getLastModifiedTime(Path, LinkOption...) FileTime 最終更新時刻を返す
setLastModifiedTime(Path path, FileTime time) Path 最終更新時刻を設定する

更新時刻は java.nio.file.attribute.FileTime 型のオブジェクトを介して取得設定します。 FileTime クラスの定義はこんなの:

package java.nio.file.attribute;

import java.util.concurrent.TimeUnit;

public class FileTime implements Comparable<FileTime>{

    // このオブジェクトが表す時刻を long 値で返す
    public long to(TimeUnit unit)
    public long toMillis()

    // 【static ファクトリ・メソッド】 引数の long 値で表される時刻の FileTime オブジェクトを返す
    public static FileTime from(long value, TimeUnit unit)
    public static FileTime fromMillis(long value)

    // Object クラスのメソッドをオーバーライド
    public boolean equals(Object obj)    // null でなく同じ時刻なら true
    public int hashCode()
    public String toString()

    // Comparable インターフェースのメソッドをオーバーライド
    public int compareTo(FileTime other)    // 時間順序で大小評価

FileTime オブジェクトを新たに生成したい場合は、FileTime クラスの static メソッド

  • FileTime#fromMillis() メソッド
  • FileTime#from() メソッド

を使います。 java.io.File のように long 値で簡単に更新時刻の操作ができないのがちょっと面倒な気もするけど・・・ まぁ、サンプル・コードを見ていきましょう:

import java.nio.file.Files
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
import java.time.ZoneId
import java.time.ZonedDateTime

// 準備
def src = Files.createTempFile(null, null)

// 時刻の取得
println Files.getLastModifiedTime(src)
    // 「2012-05-25T00:00:00Z」などと表示
    // FileTime#toString() がオーバーライドされてるので見やすい出力

// 時刻の更新
def day = Date.parse("yyyy-MM-dd@hh:mm:ss", "1999-12-31@00:00:00")  // Groovy!
def d = FileTime.fromMillis(day.getTime())
    // FileTime#fromMillis() で FileTime オブジェクト取得

Files.setLastModifiedTime(src, d)
println Files.getLastModifiedTime(src)
    // 「1999-12-30T15:00:00Z」と表示(タイムゾーンのため時刻がズレている)

// 【追記】 Java8 Date and Time API の Instant オブジェクト関連
// 時刻の取得
def creationTime = Files.readAttributes(src, BasicFileAttributes.class).creationTime()
def instant = creationTime.toInstant()  // FileTime → Instant
def dt1 = ZonedDateTime.ofInstant(instant, ZoneId.of("Asia/Tokyo"))
println dt1  //「2012-05-25T09:00:00+09:00[Asia/Tokyo]」などと表示

// 時刻の更新
def dt2 = ZonedDateTime.parse("2015-10-20T00:00+09:00[Asia/Tokyo]")
Files.setLastModifiedTime(src, FileTime.from(dt2.toInstant()))
    // Instant → FileTime
println Files.getLastModifiedTime(src)  // 「2015-10-19T15:00:00Z」と表示

Java8 から Date and Time APIjava.time.Instant オブジェクトに関連するメソッド

  • FileTime#from(Instant) (static メソッド) : Instant → FileTime
  • FileTime#toInstant() : FileTime → Instant

が追加されたのでサンプルコードに少しコードを追加しました。

ファイル所有者

次はファイル所有者を取得・設定するメソッド。

メソッド 返り値 説明
getOwner(Path, LinkOption...) UserPrincipal ファイル所有者を取得する
setOwner(Path, UserPrincipal) Path ファイル所有者を設定する

ファイル所有者は java.nio.file.attribute.UserPrincipal を介して設定します:

package java.nio.file.attribute;

public interface UserPrincipal{}

UserPrincipal オブジェクトを新たに生成したい場合は java.nio.file.attribute.UserPrincipalLookupService オブジェクのメソッド

  • UserPrincipalLookupService#lookupPrincipalByName(String)
  • UserPrincipalLookupService#lookupPrincipalByGroupName(String)

のどちらかを呼び出します。 メソッドを呼び出す UserPrincipalLookupService オブジェクトを取得するには、FileSystem#getUserPrincipalLookupService() メソッドを使いますが、FileSystem オブジェクト自体は FileSystems#getDefault() メソッドを使います:

import java.nio.file.Files
import java.nio.file.FileSystems

// 準備
def src = ...

// getter
println Files.getOwner(src)

//setter
def lookupService = FileSystems.getDefault().getUserPrincipalLookupService()
    // UserPrincipalLookupService オブジェクトを取得
def admin = lookupService.lookupPrincipalByName("Administrator")    // UserPrincipal を取得
Files.setOwner(src, admin)
println Files.getOwner(src)
  • UserPrincipalLookupService オブジェクトを取得するのは結構マワリクドイですが、定型句として使うしかなさそうです。

POSIX ファイル権限

最後は POSIX ファイル権限の取得・設定。 UNIX 系の OS 用です。

メソッド 返り値 説明
getPosixFilePermissions(Path, LinkOption...) Set ファイル所有者を取得する
setPosixFilePermissions(Path, Set) Path ファイル所有者を設定する

POSIX ファイル権限は java.nio.file.attribute.PosixFilePermission のセット (Set) を介して設定します。 ちなみに

  • java.nio.file.attribute.PosixFilePermission ・・・ 列挙型
  • java.nio.file.attribute.PosixFilePermissions ・・・ クラス(ユーティリティクラス)

です。 それぞれの型定義は以下のようになってます:

package java.nio.file.attribute;

public enum PosixFilePermission{
    OWNER_READ,
    OWNER_WRITE,
    OWNER_EXECUTE,

    GROUP_READ,
    GROUP_WRITE,
    GROUP_EXECUTE,

    OTHERS_READ,
    OTHERS_WRITE,
    OTHERS_EXECUTE
}

public final class PosixFilePermissions{
    // Set<PosixFilePermission> -> FileAttribute
    public static
    FileAttribute<Set<PosixFilePermission>> asFileAttribute(Set<PosixFilePermission> perms);

    // String -> Set<PosixFilePermission>
    public static Set<PosixFilePermission> fromString(String perms);

    // Set<PosixFilePermission> -> String
    public static String toString(Set<PosixFilePermission> perms):
}

ではサンプル・コード:

import java.nio.file.Files
import java.nio.file.attribute.PosixFilePermissions
import java.nio.file.attribute.PosixFilePermission
import static java.nio.file.attribute.PosixFilePermission.*
    // PosixFilePermission の定数は static import

// 準備
def src = ...

// PosixFilePermissions のサンプル
assert "rwxr-x---" == PosixFilePermissions.toString(
    EnumSet.of(OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_EXECUTE))
    // enum 定数のセットは EnumSet#of() で。

try{
    // getter
    println Files.getPosixFilePermissions(src)
    
    // setter
    // enum (PosixFilePermission) で
    Files.setPosixFilePermissions(src, EnumSet.of(
            OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_EXECUTE))
    println Files.getPosixFilePermission(src)
    
    // 文字列で
    Files.setPosixFilePermissions(src, PosixFilePermissions.fromString("rwxr-x---"))
    println Files.getPosixFilePermission(src)

}catch(UnsupportedOperationException ex){
    ex.printStackTrace()
}
  • 列挙型 PosixFilePermission の定数(OWNER_READ とか)は static import してます
  • 列挙型のセットは EnumSet#of() で作るのが手軽です
  • POSIX ファイル権限をサポートしてない場合(Windows とか)、UnsupportedOperationException が投げられます

【追記・修正】
FileTime クラスの Java8 Date and Time API 関連のメソッドに関して少し追記しました。

プログラミングGROOVY

プログラミングGROOVY

*1:UNIX ではピリオド '.' で始まる、Windows は hidden 属性がセットされているファイル、など。

*2:Groovy では特に使う必要がないので。