niszetの日記

細かい情報を載せていくブログ

(R) fsパッケージでpathまわりを使いこなしたい (1回目)

ファイルのパスの情報を扱うならfsパッケージを使うと便利。

さて、fsパッケージのお話です。前回から結構あいてしまいました。

niszet.hatenablog.com

fsパッケージでファイルのpathについて扱う関数はpath_***という名前になっています。
pathで始まる関数は下記のようです。

ls(envir = asNamespace("fs"), pattern = "^path_*")
#>  [1] "path"            "path_"           "path_abs"       
#>  [4] "path_common"     "path_dir"        "path_expand"    
#>  [7] "path_expand_r"   "path_ext"        "path_ext_remove"
#> [10] "path_ext_set"    "path_ext<-"      "path_file"      
#> [13] "path_filter"     "path_home"       "path_home_r"    
#> [16] "path_join"       "path_norm"       "path_package"   
#> [19] "path_real"       "path_rel"        "path_sanitize"  
#> [22] "path_split"      "path_temp"       "path_tidy"

ここから、exportされている関数について順に見ていきます。

ファイルパスについては原則としてそのファイルパスに実際にディレクトリやファイルが存在してもしなくてもエラーになりません。
また、区切り文字は"/"で、Windowsの"\"の形で与えても動作し、出力は全て"/"に変更されます(書式が環境に依存しない点がfsパッケージの良いところの1つです)
path()関数は任意の数の引数を...として受け取りますが、他は原則としてc()でまとめて与えてあげないといけません。
その代わり、まとめさえすればいくつ与えても同じ処理をしてくれますので、変数にまとめてから与えれば一度に処理されるためコードも見やすく実行速度も良いというコードの出来あがりですね。

path()

与えられた引数からファイルパスを構成して返してくれます。hereパッケージのhere()と同様の動きですね(fs::pathは拡張子も与えられるが)

## プロジェクトのroot(今回はC:/Users/niszet/Documents/r work/test4blog/)を起点としたファイルパスを返す)
here::here("hoge", "fuga")
#> [1] "C:/Users/niszet/Documents/r work/test4blog/hoge/fuga"

Helpにある実行例をコピペ。hereと違って、プロジェクトのrootを起点としたパスになりませんが、そのための関数は別途あります。この結果をhere()に与えても良いですね。

fs::path("foo", "bar", "baz", ext = "zip")
#> foo/bar/baz.zip

このようにすっきりと書けます。
わざわざstringrパッケージを使って書かなくても良いわけです。便利ですね。

stringr::str_c(c(stringr::str_c(c("foo", "bar", "baz"), collapse = "/"), 
    "zip"), collapse = ".")
#> [1] "foo/bar/baz.zip"

path_abs()

こちらは絶対パスで返してくれる関数。起点は引数startに与え、デフォルトは現在いるディレクト.になっています(下記では明示的に与えていますが。現在いるディレクトリはプロジェクトのルートにしています)

fs:::path_abs(path = "a", start = ".")
#> [1] C:/Users/niszet/Documents/r work/test4blog/a

あまり意味はないですが、プロジェクトルートを起点として明示すればこのようになります。(これはhere()に直接渡した方が良いが)

fs:::path_abs(path = "a", start = here::here())
#> [1] C:/Users/niszet/Documents/r work/test4blog/a

相対パス <-> 絶対パス変換は地味に使うので、こういうのがあると便利ですね。絶対パス->相対パスpath_rel()を参照してください。

path_common()

任意の数のpathを表す文字列を受け取り、その共通部分を返します。例えば下記の例であれば、"aaa/bbb"までが共通ですのでこの文字列が返ってきます。

path("aaa", "bbb" , "ccc")
#> aaa/bbb/ccc
path("aaa", "bbb" , "ddd")
#> aaa/bbb/ddd
path_common(c(path("aaa", "bbb" , "ccc"), path("aaa", "bbb" , "ddd")))
#> aaa/bbb

path_dir()

パスのディレクトリ部分を返す関数。実際には最後の"/"からうしろを削除する動きをしているようなので、そのパスがディレクトリを示している可能性がある場合はあらかじめis_dir()を使ってそのパスは弾いておきましょう

path_dir(c("aaa/bbb/ccc","aaa/bbb/ddd"))
aaa/bbb aaa/bbb 

path_expand(), path_expand_r()

~を展開してくれます。

path_expand("~")
#> C:/Users/niszet
path_expand_r("~")
#> C:/Users/niszet/Documents

path_expand()Windows上の挙動はpath.expand()とは異なるようです。path_expand_r()はその挙動を再現した動きをするようです。
詳細についてはHelpを参照してください。

path.expand("~")
#> [1] "C:/Users/niszet/Documents"

path_ext()

拡張子を取り出す関数。

path_ext("aaa/bbb.txt")
#> [1] "txt"

path_ext_remove()

拡張子だけを削除する関数。

path_ext_remove("aaa/bbb.txt")
aaa/bbb

これらの関数は、右から見てはじめて出てきた"."から右を拡張子、左を残りとしているので、

path_ext_remove("aaa.bbb/ccc/ddd")
#> aaa

path_ext("aaa.bbb/ccc/ddd")
#> [1] "bbb/ccc/ddd"

といった挙動になります。気をつけましょう。

path_ext_set()

拡張子を与えます。既にある場合は置き換えます。

path_ext_set("aaa/bbb", "txt")
#> aaa/bbb.txt
path_ext_set("aaa/bbb.zip", "txt")
#> aaa/bbb.txt

これも先の例と同様の拡張子の判断なので注意。

path_file()

ファイル部分を返す。

path_file("aaa/bbb.txt")
#> bbb.txt

これも最後の"/"から右を返す、という挙動なので拡張子がない場合は下記のようになる。

path_file("aaa/bbb")
#> bbb

path_filter()

ファイルパスに対して、正規表現を使って絞り込むことが出来る。regexpだけじゃなくglobでもよい。

## 英字1文字にマッチし、そのあとにその文字が1文字以上続く、というパタン(要するにaa, aaa, aaaa, ..., bb, bbb, ... のようなパタンがあればマッチ)
path_filter(c("aaa/bbb", "abc/def"), regexp = "(\\w)\\1+")
#> aaa/bbb

また、invert = TRUEにするとマッチしないものが返ってくる。

path_filter(c("aaa/bbb", "abc/def"), regexp = "(\\w)\\1+", invert = TRUE)
#> abc/def

これも地味に便利(正規表現を書き間違えてハマるまでが様式美)

量が多くなってきたので、ここで一旦切ります。
path_周りはようやく半分、です。折り返し。

fsパッケージ、ファイルの操作・処理を行うときなどには必須になります。是非使いこなしましょう!1 Enjoy!!


  1. 私がね…