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

时间:2015-01-05 13:29:18

标签: r

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

我的用例是我的iTunes Music文件夹:

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:

  • 您可以将所需的输出视为列表结构,其中每个文件/文件夹名称仅在实际文件/文件夹中出现。
  • 作为一个最好的案例,我希望找到一个足够灵活的解决方案,允许不同的文件夹级别,并且不需要像文件夹深度那样多的显式 lapply调用

3 个答案:

答案 0 :(得分:3)

假设您的目录结构始终为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)

它仍然不完全通用:只要文件夹包含子文件夹,算法就会进入这些文件夹,而不用存储可能同样存在于列表中相同深度的文件。

答案 1 :(得分:3)

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

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)

答案 2 :(得分:0)

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 )

我将艺术家和专辑/标题放入数据框,然后使用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])