मैं एक उपनिर्देशिका में सभी फ़ाइलों को स्कैला में कैसे सूचीबद्ध करूं?


90

क्या किसी निर्देशिका में फ़ाइलों को पुन: सूचीबद्ध करने का एक अच्छा "स्काला-एस्क" (मुझे लगता है कि मेरा मतलब कार्यात्मक है) का तरीका है? किसी विशेष पैटर्न के मिलान के बारे में क्या?

उदाहरण के लिए पुनरावर्ती सभी फाइलों में मेल खाता "a*.foo"है c:\temp

जवाबों:


112

स्कैला कोड आमतौर पर I / O से निपटने के लिए जावा कक्षाओं का उपयोग करता है, जिसमें रीडिंग डायरेक्टरीज़ भी शामिल हैं। तो आपको कुछ इस तरह करना होगा:

import java.io.File
def recursiveListFiles(f: File): Array[File] = {
  val these = f.listFiles
  these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
}

आप सभी फ़ाइलों को एकत्र कर सकते हैं और फिर एक regex का उपयोग करके फ़िल्टर कर सकते हैं:

myBigFileArray.filter(f => """.*\.html$""".r.findFirstIn(f.getName).isDefined)

या आप पुनरावर्ती को पुनः खोज में शामिल कर सकते हैं:

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))
}

7
चेतावनी: मैंने इस कोड को चलाया और कभी-कभी f.listFiles शून्य हो जाता है (पता नहीं क्यों, लेकिन मेरे मैक पर ऐसा होता है) और पुनरावर्तीListFiles फ़ंक्शन क्रैश करता है। मुझे स्कैला में एक सुरुचिपूर्ण नल चेक बनाने के लिए पर्याप्त अनुभव नहीं है, लेकिन अगर ये == नल मेरे लिए काम करता है तो एक खाली सरणी लौटाता है।
जनवरी

2
@Jan - listFilesरिटर्न nullअगर fएक निर्देशिका को इंगित नहीं करता है या वहाँ एक आईओ त्रुटि है (कम से कम जावा कल्पना के अनुसार) है। संभवत: उत्पादन जांच के लिए एक अशक्त चेक जोड़ना बुद्धिमानी है।
रेक्स केर

5
श्वार्ज @Peter - आप अभी भी , अशक्त जांच की जरूरत है, क्योंकि यह संभव है के लिए f.isDirectoryसही वापस जाने के लिए लेकिन f.listFilesवापस जाने के लिए null। उदाहरण के लिए, यदि आपके पास फ़ाइलों को पढ़ने की अनुमति नहीं है, तो आपको ए null। दोनों चेक होने के बजाय, मैं सिर्फ एक नल चेक जोड़ूंगा।
रेक्स केर

1
वास्तव में आपको केवल अशक्त चेक की आवश्यकता होती है, f.listFilesजब रिटर्न अशक्त होता है !f.isDirectory
डंकन मैकग्रेगर

2
नल की जाँच के संबंध में, सबसे मुहावरेदार तरीका होगा नल को विकल्प और उपयोग मानचित्र में बदलना। तो असाइनमेंट वैल इन = ऑप्शन (f.listFiles) है और ++ ऑपरेटर अंत में 'getOrElse' के साथ एक मैप ऑपरेशन के अंदर है
या

47

मैं धाराओं के साथ समाधान पसंद करूंगा क्योंकि आप अनंत फ़ाइल सिस्टम पर पुनरावृति कर सकते हैं (स्ट्रीम आलसी मूल्यांकन किए गए संग्रह हैं)

import scala.collection.JavaConversions._

def getFileTree(f: File): Stream[File] =
        f #:: (if (f.isDirectory) f.listFiles().toStream.flatMap(getFileTree) 
               else Stream.empty)

खोज के लिए उदाहरण

getFileTree(new File("c:\\main_dir")).filter(_.getName.endsWith(".scala")).foreach(println)

4
वैकल्पिक वाक्य रचना:def getFileTree(f: File): Stream[File] = f #:: Option(f.listFiles()).toStream.flatten.flatMap(getFileTree)
VasiliNovikov

3
मैं आपके इरादे से सहमत हूं, लेकिन यह आपका समाधान व्यर्थ है। listFiles () पहले से ही एक पूरी तरह से मूल्यांकन की गई सरणी देता है, जो कि आपका "लेज़ीली" मूल्यांकन करता है। आपको स्ट्रीम फॉर्म स्क्रैच की आवश्यकता है, java.nio.file.DirectoryStream देखें।
डैनियल लैंगडन

7
@ डैनियल यह बिल्कुल सख्त नहीं है, यह निर्देशिकाओं को आलसी रूप से पुन: पेश करता है।
गिलाउम मास

3
मैं अपने अनंत फ़ाइल सिस्टम पर अभी कोशिश करूँगा :-)
ब्रायन एग्न्यू

खबरदार: JavaConversions अब पदावनत हो गया है। JavaConverters और AsScala सजावट का उपयोग करें।
सुमा

25

जावा 1.7 के रूप में आप सभी को java.nio का उपयोग करना चाहिए। यह निकट-से-देशी प्रदर्शन प्रदान करता है (java.io बहुत धीमा है) और इसमें कुछ उपयोगी सहायक हैं

लेकिन जावा 1.8 आप के लिए देख रहे हैं बिल्कुल परिचय:

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)

आपने फ़ाइल मिलान के लिए भी कहा। कोशिश करो java.nio.file.Files.findऔर भीjava.nio.file.Files.newDirectoryStream

यहाँ देखें प्रलेखन: http://docs.oracle.com/javase/tutorial/essential/io/walk.html


i get: त्रुटि: (38, 32) मान asScala java.util.Iterator [java.nio.file.Path] Files.walk (dir) .iterator () का सदस्य नहीं है। asSala.filter (Files.isRegularFile () _))। foreach (println)
स्टुअर्ट


11

स्काला एक बहु-प्रतिमान भाषा है। एक निर्देशिका का पुनरावृत्ति करने का एक अच्छा "स्काला-एस्क" तरीका मौजूदा कोड का पुन: उपयोग करना होगा!

मैं कॉमन्स-आईओआई का उपयोग करके एक निर्देशिका को पुनरावृत्त करने के लिए पूरी तरह से स्केला-एस्क्यू तरीका पर विचार करूंगा । आप इसे आसान बनाने के लिए कुछ निहित रूपांतरणों का उपयोग कर सकते हैं। पसंद

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))
}

11

मुझे यूरा का स्ट्रीम सॉल्यूशन पसंद है, लेकिन यह (और अन्य) छिपी हुई डायरेक्ट्रीज में रिक्रिएट करता है। हम इस तथ्य का उपयोग करके भी सरल कर सकते हैं कि listFilesगैर-निर्देशिका के लिए अशक्त रिटर्न।

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))
  })

अब हम फाइलों को सूचीबद्ध कर सकते हैं

tree(new File(".")).filter(f => f.isFile && f.getName.endsWith(".html")).foreach(println)

या बाद में प्रसंस्करण के लिए पूरी धारा का एहसास

tree(new File("dir"), true).toArray

6

Apache Commons Io का FileUtils एक लाइन पर फिट बैठता है, और यह काफी पठनीय है:

import scala.collection.JavaConversions._ // important for 'foreach'
import org.apache.commons.io.FileUtils

FileUtils.listFiles(new File("c:\temp"), Array("foo"), true).foreach{ f =>

}

मुझे टाइप जानकारी जोड़ना था: FileUtils.listFiles (नई फ़ाइल ("c: \ temp"), Array ("foo"), सत्य) .toArray (Array [फ़ाइल]) ())। Foreach / f =}}
जेसन

यह केस-संवेदी फ़ाइल सिस्टम पर बहुत उपयोगी नहीं है क्योंकि आपूर्ति किए गए एक्सटेंशन को केस से बिल्कुल मेल खाना चाहिए। ExtensionFileComparator को निर्दिष्ट करने का एक तरीका प्रतीत नहीं होता है।
ब्रेंट फॉस्ट

एक वर्कअराउंड: एरे ("फू", "फू", "पीएनजी", "पीएनजी") प्रदान करें
रेनॉड

5

किसी ने भी अभी तक 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")) 

3

Scala.tools.nsc.io पर एक नज़र डालें

निर्देशिका वर्ग पर गहरी लिस्टिंग कार्यक्षमता सहित कुछ बहुत उपयोगी उपयोगिताएं हैं।

यदि मुझे सही ढंग से याद है कि यह रेट्रोनियम द्वारा हाइलाइट किया गया था (संभवतः योगदान दिया गया था) और मानक पुस्तकालय में एक ताजा और अधिक पूर्ण कार्यान्वयन होने से पहले स्टॉपगैप के रूप में देखा गया था।


3

और यहाँ @ रिक -777 से फ़िल्टर के साथ @DuncanMcGregor से स्ट्रीम समाधान का मिश्रण है:

  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 }

यह आपको (संभावित रूप से विशाल और बहुत धीमी) सूची के बजाय एक स्ट्रीम [फ़ाइल] देता है [फ़ाइल] जबकि आप तय करते हैं कि किस प्रकार की निर्देशिकाओं को वंशावली () फ़ंक्शन के साथ पुनरावृत्ति करना है।



3

स्काला में पुस्तकालय 'scala.reflect.io' है जो प्रयोगात्मक माना जाता है लेकिन काम करता है

import scala.reflect.io.Path
Path(path) walkFilter { p => 
  p.isDirectory || """a*.foo""".r.findFirstIn(p.name).isDefined
}

3

मुझे व्यक्तिगत रूप से @Rex Kerr के प्रस्तावित समाधान की शिथिलता और सरलता पसंद है। लेकिन यहाँ क्या एक पूंछ पुनरावर्ती संस्करण की तरह लग सकता है:

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)
}

अतिप्रवाह के बारे में क्या?
norisknofun

1

यहाँ रेक्स केर का एक समान समाधान है, लेकिन एक फ़ाइल फ़िल्टर को शामिल करना:

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))
}

विधि एक सूची [फ़ाइल] लौटाती है, जो ऐरे [फ़ाइल] की तुलना में थोड़ा अधिक सुविधाजनक है। यह उन सभी निर्देशिकाओं को भी नजरअंदाज करता है जो छिपी होती हैं (यानी शुरुआत। '')।

यह आपके द्वारा चुने गए फ़ाइल फ़िल्टर का उपयोग करके आंशिक रूप से लागू किया जाता है, उदाहरण के लिए:

val srcDir = new File( ... )
val htmlFiles = findFiles( _.getName endsWith ".html" )( srcDir )

1

सबसे सरल स्काला-एकमात्र समाधान (यदि आपको स्काला कंपाइलर लाइब्रेरी की आवश्यकता नहीं है):

val path = scala.reflect.io.Path(dir)
scala.tools.nsc.io.Path.onlyFiles(path.walk).foreach(println)

अन्यथा, @ रेनॉड का समाधान छोटा और मीठा है (यदि आप अपाचे कॉमन्स फाइलयूटिल्स में मन नहीं लगाते हैं):

import scala.collection.JavaConversions._  // enables foreach
import org.apache.commons.io.FileUtils
FileUtils.listFiles(dir, null, true).foreach(println)

कहाँ dirएक java.io.File है:

new File("path/to/dir")

1

ऐसा लगता है कि किसी को भी scala-ioस्कैला-इनक्यूबेटर से पुस्तकालय का उल्लेख नहीं है ...

import scalax.file.Path

Path.fromString("c:\temp") ** "a*.foo"

या के साथ implicit

import scalax.file.ImplicitConversions.string2path

"c:\temp" ** "a*.foo"

या यदि आप implicitस्पष्ट रूप से चाहते हैं ...

import scalax.file.Path
import scalax.file.ImplicitConversions.string2path

val dir: Path = "c:\temp"
dir ** "a*.foo"

दस्तावेज़ यहाँ उपलब्ध है: http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#//ileile/glob_based_path_sets


0

यह भस्म मेरे लिए काम करता है:

  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]())(_ ++ _)
    }
  }

0

आप इसके लिए पूंछ पुनरावृत्ति का उपयोग कर सकते हैं:

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))
  }
}

-1

आप Scala के AbstractFile के बजाय Java की फाइल का उपयोग क्यों कर रहे हैं?

स्काला के एब्सट्रैक्ट के साथ, इटमैटर समर्थन जेम्स मूर के समाधान के अधिक संक्षिप्त संस्करण को लिखने की अनुमति देता है:

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)
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.