【问题标题】:Read nested folder and file names as nested list将嵌套文件夹和文件名读取为嵌套列表
【发布时间】:2015-01-05 13:29:17
【问题描述】:

我正在尝试将已定义目录的所有文件夹和文件名读取到嵌套列表中,该列表将与顶层文件夹的数量一样长,然后每个列表元素的元素数量与子列表中的元素一样多-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, "/")
})

如何正确操作?

附:

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

【问题讨论】:

    标签: r


    【解决方案1】:

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

    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)
    

    【讨论】:

    • 谢谢,本!这看起来像是一个巧妙的解决方案,并且基于初始测试,似乎可以满足我的要求。顺便说一句,我只是说我不想根据文件夹的级别手动编写 x lapply 调用。由于这是递归的,因此它符合我的意思。 (+1)
    【解决方案2】:

    假设您的目录结构始终为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)。同时,我希望有一个更通用的解决方案,如果有不同的文件夹级别或更多,我也不需要使用单独的 lapply 调用来指定每个级别。
    • 是的,我的解决方案确实既不通用也不优雅。我也会对更好的解决方案感兴趣!
    • 修改后的版本非常好,已经对第一个版本进行了很好的改进,再次感谢!让我们看看它是否可以完全概括。
    【解决方案3】:
    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])
    

    【讨论】:

    • 感谢您的回答。不幸的是,我在第二行出现错误。此外,这似乎是一个相当静态的解决方案。
    • 我不得不根据 op 猜测艺术家和专辑的位置。实际的子集可能不同
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    • 1970-01-01
    • 2018-03-31
    相关资源
    最近更新 更多