Bir dizindeki dosyaları yinelemeli olarak listelemenin iyi bir "scala-esque" (sanırım işlevsel demek istiyorum) yolu var mı? Belirli bir kalıbı eşleştirmeye ne dersiniz?
Örneğin ardışık tüm dosyalar eşleşen "a*.foo"
içinde c:\temp
.
Bir dizindeki dosyaları yinelemeli olarak listelemenin iyi bir "scala-esque" (sanırım işlevsel demek istiyorum) yolu var mı? Belirli bir kalıbı eşleştirmeye ne dersiniz?
Örneğin ardışık tüm dosyalar eşleşen "a*.foo"
içinde c:\temp
.
Yanıtlar:
Scala kodu, dizinleri okumak dahil G / Ç ile ilgilenmek için tipik olarak Java sınıflarını kullanır. Yani şöyle bir şey yapmalısın:
import java.io.File
def recursiveListFiles(f: File): Array[File] = {
val these = f.listFiles
these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}
Tüm dosyaları toplayabilir ve ardından bir normal ifade kullanarak filtreleyebilirsiniz:
myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)
Veya normal ifadeyi yinelemeli aramaya dahil edebilirsiniz:
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
döner null
eğer f
bir dizine işaret veya GÇ hatası (en azından göre Java spec) varsa etmez. Boş kontrol eklemek, üretimde kullanım için muhtemelen akıllıca olacaktır.
f.isDirectory
gerçek dönmek fakat f.listFiles
dönmek için null
. Örneğin, dosyaları okuma izniniz yoksa, bir null
. Her iki çeke sahip olmaktansa, sadece bir boş çek eklerim.
f.listFiles
null ne zaman döndürdüğü için yalnızca boş kontrolüne ihtiyacınız vardır !f.isDirectory
.
Sonsuz dosya sistemi üzerinde yineleme yapabileceğiniz için Akışlar ile çözümü tercih ederim (Akışlar tembel olarak değerlendirilen koleksiyonlardır)
import scala.collection.JavaConversions._
def getFileTree(f: File): Stream[File] =
f #:: (if (f.isDirectory) f.listFiles().toStream.flatMap(getFileTree)
else Stream.empty)
Arama örneği
getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)
def getFileTree(f: File): Stream[File] = f #:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)
Java 1.7'den itibaren hepiniz java.nio kullanıyor olmalısınız. Yerel performansa yakın performans sunar (java.io çok yavaştır) ve bazı yararlı yardımcıları vardır
Ancak Java 1.8 tam olarak aradığınız şeyi sunar:
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)
Dosya eşleştirme de istediniz. Deneyin java.nio.file.Files.find
ve ayrıcajava.nio.file.Files.newDirectoryStream
Buradaki belgelere bakın: http://docs.oracle.com/javase/tutorial/essential/io/walk.html
for (file <- new File("c:\\").listFiles) { processFile(file) }
Scala, çok paradigmalı bir dildir. Bir dizini yinelemenin iyi bir "scala-esque" yolu, var olan bir kodu yeniden kullanmaktır!
Commons- io'yu bir dizini yinelemenin mükemmel ölçeklendirilmiş bir yolunu kullanmayı düşünürdüm . Bunu kolaylaştırmak için bazı örtük dönüşümler kullanabilirsiniz. Sevmek
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))
}
Yura'nın akış çözümünü seviyorum, ancak bu (ve diğerleri) gizli dizinlerde tekrarlanıyor. listFiles
Dizin olmayan bir için null döndüren olguyu kullanarak da sadeleştirebiliriz .
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))
})
Şimdi dosyaları listeleyebiliriz
tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)
veya daha sonra işlenmek üzere tüm akışı gerçekleştirin
tree(new File("dir"), true).toArray
Apache Commons Io'nun FileUtils'i bir satıra sığar ve oldukça okunabilirdir:
import scala.collection.JavaConversions._ // important for 'foreach'
import org.apache.commons.io.FileUtils
FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f =>
}
Henüz kimse bahsetmedi 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"))
Scala.tools.nsc.io'ya bir göz atın
Burada, Directory sınıfında derin listeleme işlevini içeren çok faydalı bazı yardımcı programlar vardır.
Doğru hatırlıyorsam, bu retronym tarafından vurgulanmıştır (muhtemelen katkıda bulunmuştur) ve io standart kitaplıkta yeni ve daha eksiksiz bir uygulama almadan önce bir geçici boşluk olarak görülmüştür.
Ve burada @DuncanMcGregor'daki akış çözümünün @ Rick-777 filtresinin bir karışımı:
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 }
Bu, descendCheck () işlevi ile hangi tür dizinlerin tekrarlanacağına karar vermenize izin verirken (potansiyel olarak çok büyük ve çok yavaş) Liste [Dosya] yerine size bir Akış [Dosya] verir.
Ne dersin
def allFiles(path:File):List[File]=
{
val parts=path.listFiles.toList.partition(_.isDirectory)
parts._2 ::: parts._1.flatMap(allFiles)
}
@Rex Kerr'in önerdiği çözümün zarafetini ve sadeliğini kişisel olarak seviyorum. Ama işte bir kuyruklu yinelemeli sürüm nasıl görünebilir:
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)
}
İşte Rex Kerr'inkine benzer bir çözüm, ancak bir dosya filtresi içeriyor:
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))
}
Yöntem, Dizi [Dosya] 'dan biraz daha uygun olan bir Liste [Dosya] döndürür. Ayrıca, gizli olan tüm dizinleri de yok sayar (yani "." İle başlayan).
Seçtiğiniz bir dosya filtresi kullanılarak kısmen uygulanır, örneğin:
val srcDir = new File( ... )
val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )
Yalnızca Scala içeren en basit çözüm (Scala derleyici kitaplığına ihtiyaç duymanızın sakıncası yoksa):
val path = scala.reflect.io.Path(dir)
scala.tools.nsc.io.Path.onlyFiles(path.walk).foreach(println)
Aksi takdirde, @ Renaud'un çözümü kısa ve tatlıdır (Apache Commons FileUtils'i çekmeyi düşünmüyorsanız):
import scala.collection.JavaConversions._ // enables foreach
import org.apache.commons.io.FileUtils
FileUtils.listFiles(dir, null, true).foreach(println)
dir
Java.io. dosyası nerede :
new File("path/to/dir")
Görünüşe göre kimse scala-io
kütüphaneden scala-incubrator'dan bahsetmiyor ...
import scalax.file.Path
Path.fromString("c:\temp") ** "a*.foo"
Veya ile implicit
import scalax.file.ImplicitConversions.string2path
"c:\temp" ** "a*.foo"
Ya da implicit
açıkça istiyorsanız ...
import scalax.file.Path
import scalax.file.ImplicitConversions.string2path
val dir: Path = "c:\temp"
dir ** "a*.foo"
Belgeler burada mevcuttur: http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#!/file/glob_based_path_sets
Bu büyü benim için çalışıyor:
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]())(_ ++ _)
}
}
Bunun için kuyruk özyinelemesini kullanabilirsiniz:
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))
}
}
os-lib , Scala'da dosyaları yinelemeli olarak listelemenin en kolay yoludur.
os.walk(os.pwd/"countries").filter(os.isFile(_))
Soruda "a*.foo"
belirtilen kalıpla eşleşen tüm dosyaları yinelemeli olarak nasıl listeleyeceğiniz aşağıda açıklanmıştır :
os.walk(os.pwd/"countries").filter(_.segments.toList.last matches "a.*\\.foo")
os-lib, diğer alternatiflerden çok daha zarif ve güçlüdür. os
Kolayca taşıyabileceğiniz, yeniden adlandırabileceğiniz nesneleri döndürür . Artık hantal Java kitaplıklarıyla uğraşmanıza gerek yok.
Bu kitaplığı yerel makinenizde denemek isterseniz çalıştırabileceğiniz bir kod parçacığı:
os.makeDir(os.pwd/"countries")
os.makeDir(os.pwd/"countries"/"colombia")
os.write(os.pwd/"countries"/"colombia"/"medellin.txt", "q mas pues")
os.write(os.pwd/"countries"/"colombia"/"a_something.foo", "soy un rolo")
os.makeDir(os.pwd/"countries"/"brasil")
os.write(os.pwd/"countries"/"brasil"/"a_whatever.foo", "carnaval")
os.write(os.pwd/"countries"/"brasil"/"a_city.txt", "carnaval")
println(os.walk(os.pwd/"countries").filter(os.isFile(_)))
bunu döndürecek:
ArraySeq(
/.../countries/brasil/a_whatever.foo,
/.../countries/brasil/a_city.txt,
/.../countries/colombia/a_something.foo,
/.../countries/colombia/medellin.txt)
os.walk(os.pwd/"countries").filter(_.segments.toList.last matches "a.*\\.foo")
bunu döndürecek:
ArraySeq(
/.../countries/brasil/a_whatever.foo,
/.../countries/colombia/a_something.foo)
Os-lib'in nasıl kullanılacağı hakkında daha fazla ayrıntı için buraya bakın .
Neden Scala'nın AbstractFile'ı yerine Java'nın Dosyasını kullanıyorsunuz?
Scala'nın AbstractFile'ı ile yineleyici desteği, James Moore'un çözümünün daha kısa bir versiyonunun yazılmasına izin verir:
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)
}