将嵌套文件夹和文件名读取为嵌套列表
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.:
- 您可以将所需的输出视为 list-structure,其中每个 file/folder 名称仅在实际 file/folder 中出现。
- 作为最好的情况,我希望找到一个足够灵活的解决方案,以允许不同的文件夹级别,并且不需要那么多 explicit lapply调用文件夹深度
假设您的目录结构始终为 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)
我正在尝试将定义目录的所有文件夹和文件名读入一个嵌套列表,该列表的长度与顶层文件夹的数量一样长,然后每个列表元素的元素数量与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.:
- 您可以将所需的输出视为 list-structure,其中每个 file/folder 名称仅在实际 file/folder 中出现。
- 作为最好的情况,我希望找到一个足够灵活的解决方案,以允许不同的文件夹级别,并且不需要那么多 explicit lapply调用文件夹深度
假设您的目录结构始终为 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)