如何将分割在不同行上的特定字符串/正则表达式匹配为单独的字符串?

时间:2019-05-17 03:31:13

标签: regex scala multiline

这是从我编写的代码中获得的内容的演示。我正在使用Source.fromFile(filePath)懒惰地读取文件,并利用.getLines()方法以字符串的形式读取每一行并对其进行迭代以查找其中是否存在特定的单词/样式。

让我们考虑将模式匹配为“ .read”。 。如果整个模式都在同一行上,则line.contains(“。read。”)可以正常工作。 如果它以下列任何一种方式分布在后续行中,则会出现问题:

.
read.

.
read
.

.read
.

我什至无法收集List [String]中文件的全部内容,因为内存消耗太大,以至于无法利用索引来连接上一行或下一行,因为它是bufferedSource迭代器正在被使用。

val bufferedSource = Source.fromFile("C:/code.scala")
val key = ".read."
var lineCounter = 0
for (bufferedline <- bufferedSource.getLines()) {
    lineCounter+=1
    val line = bufferedline.trim
    if (line.length() != 0) {
    if(line.contains(".read."))
        println("Found pattern at : "+lineCounter)
    }
}

当模式分布在多个字符串而不是由newLine字符分隔的单个字符串中时,我不确定如何合并更改。对于解决此类问题的任何帮助将不胜感激。

注意-这只是一个简单的示例,如果要匹配的模式分布在3行中,但是在某些情况下,可能会找到一个特定的字符串“ spark.read.option”,并将其分布在5个不同的位置行。

1 个答案:

答案 0 :(得分:1)

如果我尝试这样做,我会

  1. 放弃使用getLines()。跨多行文本查找目标变得很复杂。
  2. 放弃使用正则表达式模式作为目标字符串。找到一个匹配项,里面可能有也可能没有多个\n个字符。

因此,我会使用逐个字符的搜索来寻找设定的目标。

def findInFile(charItr :Iterator[Char], target :String) :Unit = {
  assert(target.nonEmpty)
  def consumeStr(subci   :Iterator[Char]
                ,str     :String
                ,lineNum :Int
                ) :Option[(Iterator[Char],Int)] =
    if      (str.isEmpty)    Some((subci,lineNum))
    else if (!subci.hasNext) None
    else subci.next() match {
      case '\n'               => consumeStr(subci, str, lineNum + 1)
      case c if c == str.head => consumeStr(subci, str.tail, lineNum)
      case _                  => None
    }

  def loop(ci :Iterator[Char], line :Int) :Unit = if (ci.hasNext) {
    ci.next() match {
      case '\n' => loop(ci, line+1)
      case c if c == target.head =>
        val (oldci,newci) = ci.duplicate
        consumeStr(newci, target.tail, line).fold(loop(oldci, line)){
          case (itr,ln) => println(s"target found: line $line")
                           loop(itr,ln)
        }
      case _ => loop(ci, line)
    }
  }

  loop(charItr, 1)
}

这是我使用的测试文件...

xxx
x
aa
aaaa
a.b
b.c
cccc
a
aa.bb.caaa.bb.cc.dd
xxx

...以及我搜索的测试目标。

val src = io.Source.fromFile("so.txt")
findInFile(src, "aaa.bb.cc")
src.close()
//target found: line 4
//target found: line 9

好的,所以我重新调整了findInFile()的位置。

def findInFile(charItr :Iterator[Char], target :String) :List[(Int,String)] = {
  assert(target.nonEmpty)
  def consumeStr(subci   :Iterator[Char]
                ,str     :String
                ,lineNum :Int
                ) :Option[(Iterator[Char],Int)] =
    if      (str.isEmpty)    Some((subci,lineNum))
    else if (!subci.hasNext) None
    else subci.next() match {
      case '\n'               => consumeStr(subci, str, lineNum + 1)
      case c if c == str.head => consumeStr(subci, str.tail, lineNum)
      case _                  => None
    }

  def loop(ci :Iterator[Char], line :Int) :List[(Int,String)] =
    if (ci.hasNext) {
      ci.next() match {
        case '\n' => loop(ci, line+1)
        case c if c == target.head =>
          val (oldci,newci) = ci.duplicate
          consumeStr(newci, target.tail, line).fold(loop(oldci, line)){
            (line,target) :: (loop _).tupled(_)
          }
        case _ => loop(ci, line)
      }
    } else Nil

  loop(charItr, 1)
}

有了这个,并使用与以前相同的测试文件,我们可以执行以下操作:

val src1 = io.Source.fromFile("so.txt")  //open twice
val src2 = io.Source.fromFile("so.txt")

"a{2,3}.bb.c[ac]".r                                   //regex pattern
                 .findAllIn(src1.getLines().mkString) //all matches
                 .toSeq.distinct                      //remove duplicates
                 .foldLeft(src2.duplicate -> List.empty[(Int,String)]){
                   case (((srcA,srcB),lst),str) =>
                     (srcA.duplicate, lst ++ findInFile(srcB,str))
                 }._2.sorted
//res0: List[(Int, String)] = List((4,aa.bb.cc), (4,aaa.bb.cc), (8,aaa.bb.ca), (9,aa.bb.cc), (9,aaa.bb.cc))

src1.close()  //close up and go home
src2.close()

这个想法是首先将整个文件以String的形式读取到内存中,而不包含换行符,然后查找所有正则表达式匹配项并将它们转换为所有唯一匹配字符串的列表。然后将它们发送到findInFile()。排序并返回。

效率不高,但是可以完成工作。