将嵌套文件夹和文件名读取为嵌套列表

Read nested folder and file names as nested list

我正在尝试将定义目录的所有文件夹和文件名读入一个嵌套列表,该列表的长度与顶层文件夹的数量一样长,然后每个列表元素的元素数量与sub-directory(如果它是一个文件夹)等等,直到只有文件而没有更多文件夹的级别。

我的用例是我的 iTunes 音乐文件夹:

m <- "/Users/User/Music/iTunes/iTunes Media/Music"  # set the path to the library folder
x <- list.files(m, recursive = FALSE)               # get all artists names (folder names on top level)
# read all Albums and title of each song per album
lst <- setNames(lapply(paste(m, x, sep = "/"), list.files, recursive = T), x)  

lst中每个元素的结构现在是:

#$`The Kooks`                                       # artist name "The Kooks"
# [1] "Inside In Inside Out/01 Seaside.mp3"         # album name "Inside In Inside Out", title "01 Seaside.mp3"
# [2] "Inside In Inside Out/02 See The World.mp3"                 
#...                           
#[16] "Konk/01 See The Sun.mp3"                     # second album of The Kooks
#[17] "Konk/02 Always Where I Need To Be.mp3"               

我想做的是为每个艺术家制作嵌套列表的条目,因此在示例中会有列表元素 $TheKooks,它有 2 个(子)列表(1 个用于每个专辑):$Inside In Inside Out$Konk 并且每个专辑列表中都有一个标题名称向量(没有专辑名称)。

我在 SO 上(还)找不到正确的答案并尝试(未成功),除此之外:

list.files(m, recursive = TRUE)

lapply(lst, function(l) {
  strsplit(l, "/")
})

如何正确操作?

P.S.:

假设您的目录结构始终为 artist/album/songs,此解决方案应该有效。如果某些目录更深(或更浅),你将得不到你想要的。

首先获取目录列表(即艺术家列表):

artists <- list.dirs(path=m,recursive=FALSE,full.names=FALSE)

然后我创建嵌套列表:

lapply(artists,function(dir) {
  albums <- list.dirs(path=paste0(m,"/",dir),recursive=FALSE,full.names=FALSE)
  album.list <-
      lapply(albums,function(dir2) {
      list.files(path=paste0(m,"/",dir,"/",dir2))
  })
  names(album.list) <- albums
  album.list
})

最后,我将列表的顶层命名为:

names(music.list) <- artists

专辑级别的工作原理与艺术家级别相同:我获取目录(对应于专辑),然后我列出其中的文件(对应于歌曲),最后,我用专辑名称命名列表元素。

编辑: 正如 docendo discimus 指出的那样,上述解决方案并不通用。以下递归解决方案应该以更优雅的方式完成工作:

rfl <- function(path) {
  folders <- list.dirs(path,recursive=FALSE,full.names=FALSE)
  if (length(folders)==0) list.files(path)
  else {
    sublist <- lapply(paste0(path,"/",folders),rfl)
    setNames(sublist,folders)
  }  
}
rfl(m)

仍然不完全通用:只要一个文件夹包含子文件夹,算法就会下降到这些文件夹中而不将可能也存在于相同深度的文件存储到列表中.

files = list.files(m ,recursive = T)

music.df <- data.frame( artist = sapply(strsplit(files, '/'), '[[', 7), song =  paste( sapply(strsplit(files, '/'), '[[', 8), sapply(strsplit(files, '/'), '[[', 9) , sep = '/' ) )

out <- split( music.df[,2] , f = music.df$artist )

我将艺术家和 album/title 放入一个数据框中,然后使用 split 将数据框拆分为艺术家列表

或者您可以制作 strsplit 输出的数据框,然后在数据框上使用 split。 (ncol 会因文件夹的深度而异)

files = list.files(m ,recursive = T) 
music.df <- data.frame(matrix(unlist(strsplit(files, '/')), ncol = 9, byrow = T) )
out <- split( music.df[,9] , f = music.df[7:8])

以下函数识别目录中的文件和文件夹。然后它会为每个已识别的文件夹再次调用自身,创建一个包含找到的所有文件和子文件夹的列表。

fileFun <- function(theDir) {
    ## Look for files (directories included for now)
    allFiles <- list.files(theDir, no.. = TRUE)
    ## Look for directory names
    allDirs <- list.dirs(theDir, full.names = FALSE, recursive = FALSE)
    ## If there are any directories,
    if(length(allDirs)) {
        ## then call this function again
        moreFiles <- lapply(file.path(theDir, allDirs), fileFun)
        ## Set names for the new list
        names(moreFiles) <- allDirs
        ## Determine files found, excluding directory names
        outFiles <- allFiles[!allFiles %in% allDirs]
        ## Combine appropriate results for current list
        if(length(outFiles)) {
            allFiles <- c(outFiles, moreFiles)
        } else {
            allFiles <- moreFiles
        }
    }
    return(allFiles)
}
## Try with your directory?
fileFun(m)