【问题标题】:Scala regex splitting on InputStreamInputStream 上的 Scala 正则表达式拆分
【发布时间】:2016-11-06 19:27:05
【问题描述】:

我正在解析一个资源文件并在空行上拆分,使用以下代码:

val inputStream = getClass.getResourceAsStream("foo.txt")
val source = scala.io.Source.fromInputStream(inputStream)
val fooString = source.mkString
val fooParsedSections = fooString.split("\\r\\n[\\f\\t ]*\\r\\n")

我相信这是将输入流作为一个完整的字符串拉入内存,然后在正则表达式上拆分。这适用于我正在解析的相对较小的文件,但这并不理想,我很好奇如何改进它--

两个想法是:

  1. 逐行读取输入流,并有一个我建立的段缓冲区,在空行上拆分
  2. 基于小型有限状态机逐个字符读取流并解析段

但是,如果可能,我希望不维护可变缓冲区。

有什么建议吗?这只是一个个人有趣的项目,我想学习如何以有效和实用的方式做到这一点。

【问题讨论】:

    标签: performance scala functional-programming inputstream


    【解决方案1】:

    您可以使用Stream.span 方法获取空行之前的前缀,然后重复。这是一个辅助函数:

    def sections(lines: Stream[String]): Stream[String] = {
      if (lines.isEmpty) Stream.empty
      else {
        // cutting off the longest `prefix` before an empty line
        val (prefix, suffix) = lines.span { _.trim.nonEmpty }
        // dropping any empty lines (there may be several)
        val rest = suffix.dropWhile{ _.trim.isEmpty }
    
        // grouping back the prefix lines and calling recursion
        prefix.mkString("\n") #:: sections(rest)
      }
    }
    

    请注意,Stream 的方法 #:: 是惰性的,在需要之前不会评估正确的操作数。以下是如何将其应用于您的用例:

    val inputStream = getClass.getResourceAsStream("foo.txt")
    val source = scala.io.Source.fromInputStream(inputStream)
    val parsedSections = sections(source.getLines.toStream)
    

    Source.getLines 方法返回 Iterator[String] 我们将其转换为 Stream 并应用辅助函数。如果您在处理途中的线路组并且不需要存储它们,您也可以在最后调用.toIterator。有关详细信息,请参阅 Stream 文档。

    编辑

    如果你还想使用正则表达式,可以将上面函数中的.trim.nonEmpty改为使用Stringmatches方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多