scalaのサブディレクトリにあるすべてのファイルを一覧表示するにはどうすればよいですか?


90

ディレクトリ内のファイルを再帰的に一覧表示するための適切な「スカラ風」(機能的だと思います)の方法はありますか?特定のパターンのマッチングについてはどうですか?

例えば、再帰的にすべてのマッチングをファイル"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関数がクラッシュします。私はscalaでエレガントなnullチェックを構築するのに十分な経験はありませんが、これらの== nullがうまくいった場合は空の配列を返します。
ヤン

2
@Jan- がディレクトリを指していない場合、または(少なくともJava仕様に従って)IOエラーが発生した場合にlistFiles返されます。nullチェックを追加することは、おそらく本番環境での使用に適しています。nullf
レックスカー

5
シュワルツ@Peter -あなたはまだことが可能であるため、ヌルのチェックを必要とf.isDirectorytrueを返すことなく、f.listFiles返すことnull。たとえば、ファイルを読み取る権限がない場合は、を取得しnullます。両方のチェックを行うのではなく、1つのnullチェックを追加します。
Rex Kerr、

1
実際にあなただけの、ヌルチェックが必要なf.listFilesリターンがする場合はnull !f.isDirectory
Duncan McGregor

2
Nullチェックに関して、最も慣用的な方法は、nullをオプションに変換してマップを使用することです。したがって、割り当てはval these = Option(f.listFiles)であり、++演算子はマップ操作内にあり、末尾に「getOrElse」が含まれています
または、Peles

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 2014

3
私はあなたの意図に同意しますが、これはあなたの解決策には意味がありません。listFiles()は既に完全に評価された配列を返します。これは、toStreamで「遅延」評価されます。ストリーム形式のスクラッチが必要です。java.nio.file.DirectoryStreamを探してください。
Daniel Langdon

7
@ダニエルそれは絶対に厳密ではありません、それは遅延してディレクトリを再帰します。
GuillaumeMassé2014年

3
私は今、私の無限のファイルシステムでそれを試してみましょう:-)
Brian Agnew

注意:JavaConversionsは非推奨になりました。JavaConvertersとasScala装飾を使用してください。
スマ

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


i:get: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)
スチュアート2017


11

Scalaはマルチパラダイム言語です。ディレクトリを反復する「スカラ風」の良い方法は、既存のコードを再利用することです!

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は1行に収まり、非常に読みやすくなっています。

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ウィーラー

提供される拡張子は大文字と小文字が完全に一致する必要があるため、大文字と小文字を区別するファイルシステムではあまり役に立ちません。ExtensionFileComparatorを指定する方法がないようです。
ブレントファウスト

回避策: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クラスの詳細リスト機能を含む、いくつかの非常に便利なユーティリティがあります。

私が正しく覚えている場合、これはレトロニムによって強調表示(おそらく貢献)され、標準ライブラリで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]が提供され、descendCheck()関数で再帰するディレクトリの種類を決定できます。


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 2016年

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

このメソッドはList [File]を返します。これは、Array [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-ioscala-incubratorのライブラリについて誰も触れていないようです...

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_pa​​th_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.