【问题标题】:Scala : cleanest way to recursively parse files checking for multiple stringsScala:递归解析文件检查多个字符串的最简洁方法
【发布时间】:2011-01-07 20:27:51
【问题描述】:

我想编写一个 Scala 脚本来递归处理目录中的所有文件。对于每个文件,我想看看是否存在字符串出现在第 X 行和第 X - 2 行的情况。如果发生这种情况,我想停止处理该文件,并将该文件名添加到地图文件名的出现次数。我今天刚开始学习 Scala,文件递归代码正常工作,需要一些关于字符串搜索的帮助,这是我目前所掌握的:


import java.io.File
import scala.io.Source

val s1= "CmdNum = 506"
val s2 = "Data = [0000,]"

def processFile(f: File) {
  val lines = scala.io.Source.fromFile(f).getLines.toArray
  for (i = 0 to lines.length - 1) {
    // want to do string searches here, see if line contains s1 and line two lines above also contains s1
    //println(lines(i))
  }
}

def recursiveListFiles(f: File): Array[File] = {
  val these = f.listFiles
  if (these != null) {
    for (i = 0 to these.length - 1) {
      if (these(i).isFile) {
        processFile(these(i))
      }
    }
    these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
  }
  else {
    Array[File]()
  }
}

println(recursiveListFiles(new File(args(0))))

【问题讨论】:

    标签: string file scala


    【解决方案1】:

    你可以这样做:

    def processFile(f: File) {
      val src = Source.fromFile(f)
      val hit = src.getLines().sliding(3).exists{ 
        case List(l0, l1, l2) => l0.contains(s1) && l2.contains(s1)
        case _ => false
      }
      src.close
      // do something depending on hit like adding to a Map
    }
    

    首先您不需要转换为数组,您可以保留迭代器以仅读取查找匹配所需的行。

    您可以使用sliding 获取派生迭代器,该迭代器使用3 行滑动窗口,您可以在其中查找在线ii+2 上的字符串。

    exists 测试此滑动迭代器的元素是否满足谓词。为方便起见,case 会将滑动窗口元素中的 3 行模式匹配为 3 个 val。 我必须使用 REPL 来找出滑动真正返回的类型

    最后别忘了关闭src。

    如果需要出现次数:

      val count = src.getLines().sliding(3).filter{ 
        case List(l0, l1, l2) => l0.contains(s1) && l2.contains(s1)
        case _ => false
      }.size
    

    您过滤出现的事件,然后获取大小...

    针对短于 3 行的文件的匹配错误进行了编辑

    【讨论】:

    • 感谢您的回复,我试过了,但在处理文件时出现异常:
    • scala.MatchError: List(2010-05-31 17:31:06.015 UTC+0000 INFO [xxx-HostSy ncThread-Runnable->HostSync-176666318810351] estation.services.timesync.TimeSync - 成功与主机“www04.xxx.com:80”的时间同步:将 1275327066705 的响应解析为 2010 年 5 月 31 日星期一 17:31:06 UTC 的系统时间)在 Main$$anon$1$$anonfun$1.apply(506.scala:13) at Main$$anon$1$$anonfun$1.apply(506.scala:13) at scala.collection.Iterator$class.exists(Iterator.scala:655) at scala.collection.Iterator$GroupedIterator.exists(Iterator.scala:第772章)
    • @fred,我添加了case _ => false 来处理短文件。
    • 可以说,我认为滑动 3 返回少于 3 个元素的列表是一个错误。
    • 谢谢大家,干得好。这个新奇的 Scala 确实擅长用很少的 LOC 表达你想要的东西 :-)
    【解决方案2】:

    这里有另一种方法:

    import java.io.File
    import scala.io.Source
    
    val s1= "CmdNum = 506"
    
    def filesAt(f: File): Array[File] = if (f.isDirectory) f.listFiles flatMap filesAt else Array(f)
    
    def filterFiles(arr: Array[File]) = arr filter (
        Source
        fromFile _
        getLines ()
        sliding 3
        exists { 
            case List(l1, l2, l3) => List(l1, l3) forall (_ contains s1)
            case _ => false
        }
    )
    
    println(filterFiles(filesAt(new File(args(0)))))
    

    虽然我承认我作弊了一点。我实际上不得不写这个而不是Source fromFile _

    Source.fromFile(_)(scala.io.Codec.ISO8859)
    

    因为,否则,Scala 会拒绝无效的 UTF-8 编码。

    【讨论】:

      【解决方案3】:

      它需要改进以处理短于 3 行的文件,但在第一次尝试时,我会尝试这样的事情:

      def checkFile(file: File) = {
        val lines = ...
        (lines zip lines.tail.tail) exists { _1 = _2 }
      }
      

      然后

      val files = ...
      val validFiles = files filter { checkFile }
      

      抱歉这么简短,我正在手机上回答...

      【讨论】:

      • Ah 文件少于 3 行,这解释了我的答案中的匹配错误 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多