Scala의 하위 디렉토리에있는 모든 파일을 어떻게 나열합니까?


90

디렉토리에있는 파일을 재귀 적으로 나열하는 좋은 "scala-esque"(내가 기능성을 의미한다고 생각합니다) 방법이 있습니까? 특정 패턴을 일치시키는 것은 어떻습니까?

예를 들어 재귀 적으로 모든 파일이 일치 "a*.foo"에서 c:\temp.

답변:


112

Scala 코드는 일반적으로 디렉토리 읽기를 포함하여 I / O를 처리하기 위해 Java 클래스를 사용합니다. 따라서 다음과 같이해야합니다.

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

모든 파일을 수집 한 다음 정규식을 사용하여 필터링 할 수 있습니다.

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가 null을 반환하고 (이유는 모르지만 Mac에서는 그렇습니다) recursiveListFiles 함수가 충돌합니다. 나는 스칼라에서 우아한 null 검사를 구축하기에 충분한 경험이 없지만이 == null이 나를 위해 작동하면 빈 배열을 반환합니다.

2
@Jan- 디렉토리를 가리 키지 않거나 IO 오류가있는 경우 (적어도 Java 사양에 따라) listFiles반환 null합니다 f. 널 검사를 추가하는 것은 프로덕션 용도로 현명 할 것입니다.
Rex Kerr

5
@Peter Schwarz- true 를 반환 하지만 을 반환 하는 것이 가능하기 때문에 여전히 null 검사가 필요합니다 . 예를 들어 파일을 읽을 수있는 권한이없는 경우 . 두 검사를 모두 갖는 대신 하나의 null 검사 만 추가합니다. f.isDirectoryf.listFilesnullnull
Rex Kerr

1
로 사실 만, 널 검사를 필요로 f.listFiles리턴 할 때 널 (null) !f.isDirectory.
Duncan McGregor 2011

2
Null 검사와 관련하여 가장 관용적 인 방법은 null을 옵션으로 변환하고 맵을 사용하는 것입니다. 할당은 발이 = 옵션 (f.listFiles)이며, ++ 연산자는 말에 'getOrElse'와지도 작업 안에 그래서
또는에서 Peles

47

무한한 파일 시스템을 반복 할 수 있기 때문에 Streams 솔루션을 선호합니다 (스트림은 게으른 평가 컬렉션입니다)

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 2014

3
나는 당신의 의도에 동의하지만 이것은 당신의 해결책이 무의미합니다. listFiles ()는 이미 완전히 평가 된 배열을 반환합니다. 그러면 toStream에서 "게으른"평가가 수행됩니다. 스트림 양식 스크래치가 필요합니다. java.nio.file.DirectoryStream을 찾으십시오.
Daniel Langdon 2014 년

7
@Daniel 절대적으로 엄격하지 않으며 느리게 디렉토리를 반복합니다.
Guillaume Massé 2014 년

3
나는 :-) 내 무한 파일 시스템에 바로 지금을 시도하여야한다
브라이언 애그뉴에게

주의 : JavaConversions는 이제 더 이상 사용되지 않습니다. JavaConverters 및 asScala 장식을 사용하십시오.
Suma

25

Java 1.7부터는 모두 java.nio를 사용해야합니다. 네이티브에 가까운 성능을 제공하며 (java.io는 매우 느림) 유용한 도우미가 있습니다.

그러나 Java 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에서 문서를 참조하십시오.


나는 얻는다 : Error : (38, 32) value asScala is not a member of java.util.Iterator [java.nio.file.Path] Files.walk (dir) .iterator (). asScala.filter (Files.isRegularFile () _)). foreach (println)
stuart


11

Scala는 다중 패러다임 언어입니다. 디렉토리를 반복하는 좋은 "scala-esque"방법은 기존 코드를 재사용하는 것입니다!

commons-io를 사용하여 디렉토리를 반복하는 완벽하게 스칼라와 같은 방법을 고려할 것 입니다. 암시 적 변환을 사용하여 더 쉽게 만들 수 있습니다. 처럼

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

나는 yura의 스트림 솔루션을 좋아하지만 숨겨진 디렉토리로 재귀합니다. listFiles디렉토리가 아닌 경우 null을 반환 한다는 사실을 활용하여 단순화 할 수도 있습니다 .

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 (new File ( "c : \ temp"), Array ( "foo"), true) .toArray (Array [File] ()). foreach {f =>}
Jason Wheeler

제공된 확장자가 대소 문자와 정확히 일치해야하므로 대소 문자를 구분하는 파일 시스템에서는 그다지 유용하지 않습니다. ExtensionFileComparator를 지정하는 방법이없는 것 같습니다.
Brent Faust

해결 방법 : provide Array ( "foo", "FOO", "png", "PNG")
Renaud

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 살펴보기

Directory 클래스에 대한 자세한 목록 기능을 포함하여 매우 유용한 유틸리티가 있습니다.

내가 올바르게 기억한다면 이것은 retronym에 의해 강조되었고 (아마도 기고) io가 표준 라이브러리에서 새롭고 더 완전한 구현을 얻기 전에 임시 방편으로 간주되었습니다.


3

다음은 @DuncanMcGregor의 스트림 솔루션과 @ 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 }

이것은 당신에게 (잠재적으로 크고 매우 느린) List [File] 대신 Stream [File]을 제공하는 한편, 당신은 reduceCheck () 함수로 어떤 종류의 디렉토리로 재귀 할 것인지 결정할 수있게 해줍니다.


3

어때

   def allFiles(path:File):List[File]=
   {    
       val parts=path.listFiles.toList.partition(_.isDirectory)
       parts._2 ::: parts._1.flatMap(allFiles)         
   }

3

Scala에는 실험적인 것으로 간주되었지만 작업을 수행하는 라이브러리 '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

다음은 Rex Kerr와 유사한 솔루션이지만 파일 필터를 통합합니다.

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

이 메서드는 Array [File]보다 약간 더 편리한 List [File]을 반환합니다. 또한 숨겨진 모든 디렉토리 (예 : '.'로 시작)를 무시합니다.

선택한 파일 필터를 사용하여 부분적으로 적용됩니다. 예를 들면 다음과 같습니다.

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

1

가장 간단한 Scala 전용 솔루션 (Scala 컴파일러 라이브러리가 필요하지 않은 경우) :

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

그렇지 않으면 @Renaud의 솔루션은 짧고 달콤합니다 (Apache Commons FileUtils를 가져와도 괜찮다면).

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

dirjava.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#!/file/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의 File을 사용하는 이유는 무엇입니까?

Scala의 AbstractFile을 사용하면 반복기 지원을 통해 James Moore 솔루션의보다 간결한 버전을 작성할 수 있습니다.

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)
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.