Как сопоставить конкретную строку / регулярное выражение, разделенное на разные строки, как отдельные строки? - PullRequest
0 голосов
/ 17 мая 2019

Вот демонстрация содержимого, полученного из кода, который я написал. Я лениво читаю файл, используя 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 Ответ

1 голос
/ 17 мая 2019

Если бы я пытался это сделать, я бы:

  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().Сортировка и возврат.

Не очень эффективно, но он выполняет свою работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...