Apakah ada cara "scala-esque" (maksud saya fungsional) yang baik untuk mendaftar file secara rekursif dalam direktori? Bagaimana dengan mencocokkan pola tertentu?
Misalnya secara rekursif semua file yang cocok "a*.foo"
dengan c:\temp
.
Kode scala biasanya menggunakan kelas Java untuk menangani I / O, termasuk membaca direktori. Jadi, Anda harus melakukan sesuatu seperti:
import java.io.File
def recursiveListFiles(f: File): Array[File] = {
val these = f.listFiles
these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}
Anda dapat mengumpulkan semua file dan kemudian memfilter menggunakan regex:
myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)
Atau Anda bisa memasukkan regex ke dalam pencarian rekursif:
import scala.util.matching.Regex
def recursiveListFiles(f: File, r: Regex): Array[File] = {
val these = f.listFiles
val good = these.filter(f => r.findFirstIn(f.getName).isDefined)
good ++ these.filter(_.isDirectory).flatMap(recursiveListFiles(_,r))
}
listFiles
mengembalikannull
jikaf
tidak mengarah ke direktori atau jika ada kesalahan IO (setidaknya sesuai dengan spesifikasi Java). Menambahkan cek nol mungkin bijaksana untuk penggunaan produksi.f.isDirectory
mengembalikan true tetapif.listFiles
mengembalikannull
. Misalnya, jika Anda tidak memiliki izin untuk membaca file, Anda akan mendapatkan filenull
. Daripada memiliki kedua cek, saya hanya menambahkan satu cek nol.f.listFiles
mengembalikan null saat!f.isDirectory
.Saya lebih suka solusi dengan Streams karena Anda dapat melakukan iterasi pada sistem file tak terbatas (Stream adalah koleksi yang dievaluasi malas)
import scala.collection.JavaConversions._ def getFileTree(f: File): Stream[File] = f #:: (if (f.isDirectory) f.listFiles().toStream.flatMap(getFileTree) else Stream.empty)
Contoh pencarian
getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)
sumber
def getFileTree(f: File): Stream[File] = f #:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)
Mulai Java 1.7, Anda semua harus menggunakan java.nio. Ia menawarkan kinerja yang mendekati asli (java.io sangat lambat) dan memiliki beberapa pembantu yang berguna
Tapi Java 1.8 memperkenalkan dengan tepat apa yang Anda cari:
import java.nio.file.{FileSystems, Files} import scala.collection.JavaConverters._ val dir = FileSystems.getDefault.getPath("/some/path/here") Files.walk(dir).iterator().asScala.filter(Files.isRegularFile(_)).foreach(println)
Anda juga meminta pencocokan file. Mencoba
java.nio.file.Files.find
dan jugajava.nio.file.Files.newDirectoryStream
Lihat dokumentasi di sini: http://docs.oracle.com/javase/tutorial/essential/io/walk.html
sumber
for (file <- new File("c:\\").listFiles) { processFile(file) }
http://langref.org/scala+java/files
sumber
Scala adalah bahasa multi-paradigma. Cara "scala-esque" yang baik untuk mengiterasi direktori adalah dengan menggunakan kembali kode yang ada!
Saya akan mempertimbangkan untuk menggunakan commons-io sebagai cara yang sangat scala-esque untuk mengulang direktori. Anda dapat menggunakan beberapa konversi implisit untuk membuatnya lebih mudah. Suka
import org.apache.commons.io.filefilter.IOFileFilter implicit def newIOFileFilter (filter: File=>Boolean) = new IOFileFilter { def accept (file: File) = filter (file) def accept (dir: File, name: String) = filter (new java.io.File (dir, name)) }
sumber
Saya suka solusi streaming yura, tetapi (dan yang lainnya) muncul kembali ke direktori tersembunyi. Kita juga dapat menyederhanakan dengan memanfaatkan fakta yang
listFiles
mengembalikan null untuk non-direktori.def tree(root: File, skipHidden: Boolean = false): Stream[File] = if (!root.exists || (skipHidden && root.isHidden)) Stream.empty else root #:: ( root.listFiles match { case null => Stream.empty case files => files.toStream.flatMap(tree(_, skipHidden)) })
Sekarang kita dapat membuat daftar file
tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)
atau mewujudkan seluruh aliran untuk diproses nanti
tree(new File("dir"), true).toArray
sumber
FileUtils Apache Commons Io cocok dalam satu baris, dan cukup mudah dibaca:
import scala.collection.JavaConversions._ // important for 'foreach' import org.apache.commons.io.FileUtils FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f => }
sumber
Belum ada yang menyebutkan https://github.com/pathikrit/better-files
val dir = "src"/"test" val matches: Iterator[File] = dir.glob("**/*.{java,scala}") // above code is equivalent to: dir.listRecursively.filter(f => f.extension == Some(".java") || f.extension == Some(".scala"))
sumber
Kunjungi scala.tools.nsc.io
Ada beberapa utilitas yang sangat berguna di sana termasuk fungsionalitas daftar mendalam di kelas Direktori.
Jika saya ingat dengan benar, ini disorot (mungkin dikontribusikan) oleh retronim dan dilihat sebagai sementara sebelum io mendapatkan implementasi yang segar dan lebih lengkap di perpustakaan standar.
sumber
Dan inilah campuran solusi streaming dari @DuncanMcGregor dengan filter dari @ Rick-777:
def tree( root: File, descendCheck: File => Boolean = { _ => true } ): Stream[File] = { require(root != null) def directoryEntries(f: File) = for { direntries <- Option(f.list).toStream d <- direntries } yield new File(f, d) val shouldDescend = root.isDirectory && descendCheck(root) ( root.exists, shouldDescend ) match { case ( false, _) => Stream.Empty case ( true, true ) => root #:: ( directoryEntries(root) flatMap { tree( _, descendCheck ) } ) case ( true, false) => Stream( root ) } } def treeIgnoringHiddenFilesAndDirectories( root: File ) = tree( root, { !_.isHidden } ) filter { !_.isHidden }
Ini memberi Anda Stream [File], bukan Daftar [File] (berpotensi besar dan sangat lambat) sambil membiarkan Anda memutuskan jenis direktori mana yang akan digunakan kembali dengan fungsi descendCheck ().
sumber
Bagaimana tentang
def allFiles(path:File):List[File]= { val parts=path.listFiles.toList.partition(_.isDirectory) parts._2 ::: parts._1.flatMap(allFiles) }
sumber
Scala memiliki perpustakaan 'scala.reflect.io' yang dianggap eksperimental tetapi berfungsi
import scala.reflect.io.Path Path(path) walkFilter { p => p.isDirectory || """a*.foo""".r.findFirstIn(p.name).isDefined }
sumber
Saya pribadi menyukai keanggunan dan kesederhanaan solusi yang diusulkan @Rex Kerr. Tapi inilah versi rekursif ekornya:
def listFiles(file: File): List[File] = { @tailrec def listFiles(files: List[File], result: List[File]): List[File] = files match { case Nil => result case head :: tail if head.isDirectory => listFiles(Option(head.listFiles).map(_.toList ::: tail).getOrElse(tail), result) case head :: tail if head.isFile => listFiles(tail, head :: result) } listFiles(List(file), Nil) }
sumber
Berikut solusi yang mirip dengan Rex Kerr, tetapi menggabungkan filter file:
import java.io.File def findFiles(fileFilter: (File) => Boolean = (f) => true)(f: File): List[File] = { val ss = f.list() val list = if (ss == null) { Nil } else { ss.toList.sorted } val visible = list.filter(_.charAt(0) != '.') val these = visible.map(new File(f, _)) these.filter(fileFilter) ++ these.filter(_.isDirectory).flatMap(findFiles(fileFilter)) }
Metode ini mengembalikan Daftar [File], yang sedikit lebih nyaman daripada Array [File]. Ini juga mengabaikan semua direktori yang tersembunyi (yaitu, dimulai dengan '.').
Ini diterapkan sebagian menggunakan filter file pilihan Anda, misalnya:
val srcDir = new File( ... ) val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )
sumber
Solusi paling sederhana Scala-only (jika Anda tidak keberatan memerlukan pustaka kompilator Scala):
val path = scala.reflect.io.Path(dir) scala.tools.nsc.io.Path.onlyFiles(path.walk).foreach(println)
Jika tidak, solusi @ Renaud singkat dan manis (jika Anda tidak keberatan menggunakan FileUtils Apache Commons):
import scala.collection.JavaConversions._ // enables foreach import org.apache.commons.io.FileUtils FileUtils.listFiles(dir, null, true).foreach(println)
Di mana
dir
file java.io.:new File("path/to/dir")
sumber
Sepertinya tidak ada yang menyebutkan
scala-io
perpustakaan dari scala-inkubrator ...import scalax.file.Path Path.fromString("c:\temp") ** "a*.foo"
Atau dengan
implicit
import scalax.file.ImplicitConversions.string2path "c:\temp" ** "a*.foo"
Atau jika Anda ingin
implicit
secara eksplisit ...import scalax.file.Path import scalax.file.ImplicitConversions.string2path val dir: Path = "c:\temp" dir ** "a*.foo"
Dokumentasi tersedia di sini: http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#!/file/glob_based_path_sets
sumber
Mantra ini bekerja untuk saya:
def findFiles(dir: File, criterion: (File) => Boolean): Seq[File] = { if (dir.isFile) Seq() else { val (files, dirs) = dir.listFiles.partition(_.isFile) files.filter(criterion) ++ dirs.toSeq.map(findFiles(_, criterion)).foldLeft(Seq[File]())(_ ++ _) } }
sumber
Anda dapat menggunakan rekursi ekor untuk itu:
object DirectoryTraversal { import java.io._ def main(args: Array[String]) { val dir = new File("C:/Windows") val files = scan(dir) val out = new PrintWriter(new File("out.txt")) files foreach { file => out.println(file) } out.flush() out.close() } def scan(file: File): List[File] = { @scala.annotation.tailrec def sc(acc: List[File], files: List[File]): List[File] = { files match { case Nil => acc case x :: xs => { x.isDirectory match { case false => sc(x :: acc, xs) case true => sc(acc, xs ::: x.listFiles.toList) } } } } sc(List(), List(file)) } }
sumber
Mengapa Anda menggunakan File Java sebagai ganti AbstractFile dari Scala?
Dengan AbstractFile Scala, dukungan iterator memungkinkan penulisan versi solusi James Moore yang lebih ringkas:
import scala.reflect.io.AbstractFile def tree(root: AbstractFile, descendCheck: AbstractFile => Boolean = {_=>true}): Stream[AbstractFile] = if (root == null || !root.exists) Stream.empty else (root.exists, root.isDirectory && descendCheck(root)) match { case (false, _) => Stream.empty case (true, true) => root #:: root.iterator.flatMap { tree(_, descendCheck) }.toStream case (true, false) => Stream(root) }
sumber