08列出文件与创建目录

使用 list.files、file.path、dir.exists 和 dir.create 查找文件、构造路径并初始化批处理目录。

2026-03-08
RFilesDirectoriesBatch Processing
本章目录 · 16

批量读取数据或输出分析结果之前,脚本首先要解决两个问题:从目录中找到目标文件,以及在写出结果前准备好目录结构。

Base R 的 list.files()dir.create() 分别处理这两个步骤,不需要加载额外包。

当前工作目录

相对路径从当前工作目录开始解释:

getwd()

例如当前工作目录是一个分析项目的根目录,那么:

list.files("data")

表示列出项目下 data/ 中的内容。

脚本中应尽量从稳定的项目根目录使用相对路径,而不是反复调用 setwd() 或写死某台电脑上的绝对路径。

list.files():列出目录内容

不提供参数时,list.files() 返回当前工作目录中的文件名和目录名:

list.files()

dir() 是它的简写别名:

dir()

指定目录:

list.files("data")

默认只返回名称,不包含起始目录:

list.files("data", full.names = FALSE)

批量读取时通常需要完整路径:

list.files("data", full.names = TRUE)

使用 pattern 筛选文件名

pattern 接收正则表达式,可以按扩展名或名称模式筛选。

list.files("data", pattern = "\\.csv$")
list.files("data", pattern = "\\.txt$")

正则表达式中的 . 表示任意字符,因此匹配扩展名前的点时要写成 \\.$ 表示文件名结尾。

忽略扩展名大小写:

list.files(
  "data",
  pattern = "\\.csv$",
  ignore.case = TRUE
)

按多个扩展名筛选:

list.files(
  "data",
  pattern = "\\.(csv|txt)$",
  ignore.case = TRUE
)

也可以先列出全部名称,再用其他函数筛选:

files <- list.files("data")
csv_files <- grep("\\.csv$", files, value = TRUE)

能直接写进 pattern 的条件,通常不必再增加一次 grep()

递归查找子目录

默认情况下,list.files() 只查看当前这一层。递归搜索所有子目录:

list.files(
  "data",
  recursive = TRUE
)

与完整路径和类型筛选组合:

csv_files <- list.files(
  path = "data",
  pattern = "\\.csv$",
  recursive = TRUE,
  full.names = TRUE,
  ignore.case = TRUE
)

这是一种常见的批处理入口:返回值可以直接交给读取函数。

tables <- lapply(csv_files, read.csv)

如果子目录本身也需要包含在结果中,可以显式设置:

list.files(
  "data",
  recursive = TRUE,
  include.dirs = TRUE
)

隐藏文件与特殊目录

默认不列出以点开头的隐藏文件。需要时使用 all.files = TRUE

list.files(all.files = TRUE)

在支持 ... 目录项的系统上,可以用 no.. = TRUE 排除它们:

list.files(
  all.files = TRUE,
  no.. = TRUE
)

通常只需要普通数据文件时,保持 all.files = FALSE 即可。

使用 file.path() 构造路径

不要用字符串拼接硬写路径分隔符。file.path() 会按当前操作系统构造路径:

data_dir <- file.path("data", "raw")
output_file <- file.path("output", "tables", "summary.csv")

也可以为一组文件名批量添加目录:

files <- c("a.csv", "b.csv", "c.csv")
paths <- file.path("data", files)

list.files(full.names = TRUE) 已经完成了这一步;只有手头先有文件名时才需要再调用 file.path()

dir.create():创建目录

在当前工作目录下创建一个目录:

dir.create("codes")

创建嵌套目录时,如果父目录还不存在,使用 recursive = TRUE

dir.create(
  file.path("output", "figures"),
  recursive = TRUE
)

不设置 recursive = TRUE 时,缺少父目录会导致创建失败。

创建前检查目录

dir.exists() 用于判断目录是否已经存在:

if (!dir.exists("results")) {
  dir.create("results")
}

初始化多层输出目录:

output_dirs <- c(
  file.path("output", "figures"),
  file.path("output", "tables"),
  file.path("output", "models")
)

for (path in output_dirs) {
  if (!dir.exists(path)) {
    dir.create(path, recursive = TRUE)
  }
}

如果目录已经存在,dir.create() 默认给出 warning。也可以直接尝试创建并关闭该 warning:

dir.create(
  "results",
  recursive = TRUE,
  showWarnings = FALSE
)

需要区分“目录已存在”和“因为权限或路径错误而失败”时,应保留显式的 dir.exists() 检查。

批量读取文件

列出文件后,使用 lapply()purrr::map() 逐个读取。

csv_files <- list.files(
  "data",
  pattern = "\\.csv$",
  full.names = TRUE
)

tables <- lapply(csv_files, read.csv)

保留文件来源:

names(tables) <- basename(csv_files)

使用 purrr

tables <- purrr::map(csv_files, read.csv)

如果要把结构相同的表合并,可以在确认列名与类型一致后继续处理:

combined <- do.call(rbind, tables)

一套完整的目录初始化

data_dir <- "data"
result_dir <- file.path("output", "tables")

if (!dir.exists(result_dir)) {
  dir.create(result_dir, recursive = TRUE)
}

input_files <- list.files(
  path = data_dir,
  pattern = "\\.csv$",
  recursive = TRUE,
  full.names = TRUE,
  ignore.case = TRUE
)

if (length(input_files) == 0) {
  stop("没有找到 CSV 文件:", data_dir)
}

tables <- lapply(input_files, read.csv)

write.csv(
  do.call(rbind, tables),
  file.path(result_dir, "combined.csv"),
  row.names = FALSE
)

这个流程把路径构造、目录创建、文件发现和批量读取连在一起。真正运行批处理前,还应确认文件顺序、表结构和空结果是否符合预期。

常见问题

只拿到文件名,后续读取失败

list.files("data") 返回的只是名称。读取时要么再次拼接 data/,要么一开始就使用 full.names = TRUE

扩展名筛选匹配过宽

pattern = ".csv" 中的点会匹配任意字符。使用 pattern = "\\.csv$" 精确匹配扩展名。

递归结果失去层级信息

recursive = TRUE 时不要只保留 basename;不同子目录可能存在同名文件。保留相对路径或使用 full.names = TRUE

输出前没有创建目录

write.csv()ggsave() 等函数不会替你创建缺失的父目录。写文件之前先用 dir.create(..., recursive = TRUE) 初始化。

依赖返回顺序

list.files() 返回排序后的名称,但批处理逻辑不应把文件名顺序当作数据顺序。需要时间、批次或样本顺序时,从明确的元数据或文件名字段中解析并校验。