list.files(pattern = ".csv")みたいなのを見かけるけど、うっかりanalyze-csv.Rみたいなファイルにもマッチするよ- 厳密には
list.files(pattern = "\\.csv$")としよう - ファイル操作にはfsパッケージも便利
list.files関数のpattern引数は正規表現扱い
list.filesを使うと指定したディレクトリ以下のファイルやディレクトリを一覧できます。
# ダミーデータの用意
target_directory <- tempdir()
target_directory |>
file.path(c("analyze-csv.R", "data1.csv", "data2.csv", "README.md")) |>
file.create() |>
invisible()
# ファイル一覧の取得
list.files(target_directory)
#> [1] "analyze-csv.R" "data1.csv" "data2.csv" "README.md"さらにpattern引数を指定すると正規表現を使って、条件に一致するファイルだけを列挙できます。
「正規表現を使って」というのがポイントで、うっかりpattern = ".csv"などと指定しようものならCSVファイル以外も列挙する畏れがあります。
list.files(target_directory, pattern = ".csv")
#> [1] "analyze-csv.R" "data1.csv" "data2.csv"上記のファイル名はいずれも.csvという正規表現に部分一致しています。
.は正規表現で任意の一文字にマッチします。加えてcsvで終わる文字列かどうかを指定できていません。なので、csvの前に任意の一文字があれば、以下のいずれにもマッチします。
- data1.csv
- analyze-csv.R
- data-csv
大変だ!
正しくはpattern = "\\.csv$"と指定しましょう。
list.files(target_directory, pattern = "\\.csv$")
#> [1] "data1.csv" "data2.csv"\\を使うと正規表現で特殊な意味を持つ文字の意味を打ち消して、文字そのものとして扱えます。たとえば\\.は.が「任意の一文字」の意味ではなく、ピリオドそのものであることを示します。
また、$は、正規表現で行末を意味します。
よって\\.csv$は.csvで終わる文字列にマッチするわけですね。
ちなみにファイル検索でしばしば登場する*.csvという文字列もやはりNG。頭の*は直前の文字が0文字以上続くことを意味します。
list.files関数などRが採用する正規表現の流儀では空文字が0文字以上存在すればマッチ判定です。
0文字でいいので、実質pattern = ".csv"に相当するわけですね。
というわけでpattern引数がトリッキーですが、他にも色々便利なオプションがあるのでうまく付き合いましょう。
all.files: 隠しファイルも列挙する(既定値:FALSE)full.names: フルパスを返す(既定値:FALSE)recursive: サブディレクトリも検索する(既定値:FALSE)include.dirs: ディレクトリも列挙する(既定値:FALSE)
あるいはfsパッケージ
この手のファイル操作に特化したパッケージにfsパッケージがあります。
ファイル一覧ならfs::dir_ls関数を使いましょう。
list.files関数とよく似たことができますが、大きな違いは以下の2点です。
- 返り値がフルパス固定
- 既定動作でファイル以外にディレクトリなども列挙(
type引数で調整可能)
更に、パターンの指定方法として、regexp引数とglob引数を備えています。
regexp引数はlist.files関数のpattern引数に等価です。
fs::dir_ls(target_directory, regexp = "\\.csv$")
#> /tmp/Rtmp55uXav/data1.csv /tmp/Rtmp55uXav/data2.csv一方glob引数を使うと、*.csvといった方法で指定の拡張子にマッチさせられます。
fs::dir_ls(target_directory, glob = "*.csv")
#> /tmp/Rtmp55uXav/data1.csv /tmp/Rtmp55uXav/data2.csvENJOY!
Atusy's blog