2015-01-05 3 views
6

Я пытаюсь прочитать все имена папок и файлов определенного каталога во вложенном списке, который будет до тех пор, пока количество папок на верхнем уровне, а затем список элемент имеет столько же элементов, сколько в подкаталоге (если это папка) и т. д. до уровня, где есть только файлы и больше папок.Прочитайте вложенные папки и имена файлов в виде вложенного списка

Мой случай использования с моей папке 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

Следующая функция определяет файлы и папки в каталоге. Затем он снова вызывает себя для каждой идентифицированной папки, создавая список с найденными файлами и подпапками.

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) 
+1

Спасибо, Бен! Это похоже на опрятное решение и на основе первоначального теста, похоже, делает то, что я хотел. Кстати, я имел в виду, что мне не хотелось бы писать x lapply calls вручную в соответствии с уровнем папок. Поскольку это рекурсивный, он выполняет этот критерий, как я имел в виду. (+1) –

+0

@docendodiscimus Ах, ОК. Это проясняет ситуацию! – BenBarnes

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 

Уровень альбома работает идентично уровню исполнителя: я получаю каталоги (соответствуют в альбомы), затем я перечисляю файлы внутри (соответствующие песням) и, наконец, я называю элементы списка именами альбомов.

EDIT: Как указано в документе 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) 

Это еще не до конца целом: Пока папка содержит вложенные папки, алгоритм нисходит в эти папки без сохранения файлов, которые также могут существуют на одной глубине в списке.

+0

Спасибо за ваш ответ, Stibu! Результат действительно именно то, что я хотел получить (+1). В то же время я надеялся на более общее решение, которое также будет работать, если будут разные уровни папок или многие другие, и мне не нужно будет указывать каждый уровень с помощью отдельного звонка. –

+0

Да, мое решение действительно не является ни общим, ни элегантным. Меня тоже будут интересовать лучшие решения! – Stibu

+0

Редактированная версия очень приятная и уже довольно хорошее улучшение первого, спасибо еще раз! Посмотрим, может ли он быть полностью обобщен. –

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]) 
+0

Спасибо за ответ. К сожалению, во второй строке появляется ошибка. Кроме того, это похоже на довольно статическое решение. –

+0

Мне пришлось угадать позицию художника и альбома на основе op. фактические подсерии могут быть разными – germcd