以函数式方法拆分复杂的字符串模式(无正则表达式)

Splitting complex String Pattern (without regex) in a functional approach

我正在尝试以更惯用的函数方法拆分没有正则表达式的字符串。

case class Parsed(blocks: Vector[String], block: String, depth: Int)

def processChar(parsed: Parsed, c: Char): Parsed = {
  import parsed._
  c match {

    case '|'  if depth == 0
                =>  parsed.copy(block = "", blocks = blocks :+ block ,
                                  depth = depth)                          
    case '['  => parsed.copy(block = block + c,
                                  depth = depth + 1)
    case ']'  if depth == 1
                => parsed.copy( block = "", blocks = blocks :+ (block + c),
                                depth = depth - 1)
    case ']'  => parsed.copy(block = block + c,
                                  depth = depth - 1)
    case _    => parsed.copy(block = block + c)
  }
}

val s = "Str|[ts1:tssub2|ts1:tssub2]|BLANK|[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]|[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]|BLANK |BLANK |[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]|[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]|BLANK |BLANK |[s14|s15]"
val parsed = s.foldLeft(Parsed(Vector(), "", 0))(processChar)
parsed.blocks.size //20 
parsed.blocks foreach println

我希望得到以下结果parsed.blocks.size 应该是 12)。

Str
[ts1:tssub2|ts1:tssub2]
BLANK|
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK 
BLANK 
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK 
BLANK 
[s14|s15]

但是我得到的结果是parsed.blocks.size是20)

Str
[ts1:tssub2|ts1:tssub2]

BLANK
[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]]

[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]

BLANK
BLANK
[s2|s3|s4|INT16|INT17]
;[s5|s6|s7|INT18|INT19]

[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]
;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]

BLANK
BLANK
[s14|s15]

据我了解,这是 parenthesis balancing problem 的细微变化。然而在这种情况下 ; 将意味着某种延续。

在这种情况下我有两个问题

1) [ts1:tssub2|ts1:tssub2]之后的额外条目/space是怎么来的,

之后也是

[INT1|X.X.X.X|INT2|BLANK|BLANK||X.X.X.X|[INT3|s1]] , [INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK]

在我的结果中也是如此?

2) 目前这里 [s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]

作为两个不同的条目进入。但是,这应该合并为 [s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]

单个条目[

[[s8|s9|s10|INT20|INT21]|ts1:tssub2||]

;[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK|BLANK])

也是]。任何关于如何这样做的线索?

问题 1

出现额外的空字符串块是因为每次都是前一个案例

case ']'  if depth == 1

它添加了一个空块并减少了深度。那么我们有

case '|'  if depth == 0

这还会添加另一个空块,将之前的空块推入生成的 Vector。


在回答第二个问题之前,我想建议另一种实现此解析器的方法,这种方法稍微更惯用一些。我对当前的主要批评是使用中间对象 (Parsed) 来包装状态并在每种情况下复制它。实际上,我们不需要它:更常见的方法是使用递归函数,尤其是当涉及 depth 时。

因此,在不显着修改您的 case 的处理的情况下,它可以表示如下:

def parse(blocks: Seq[String],
          currentBlock: String,
          remaining: String,
          depth: Int): Seq[String] =
  if (remaining.isEmpty) {
    blocks
  } else {
    val curChar = remaining.head
    curChar match {
      case '|' if depth == 0 =>
        parse(blocks :+ currentBlock, "", remaining.tail, depth)
      case '[' =>
        parse(blocks, currentBlock + curChar, remaining.tail, depth + 1)
      case ']' =>
        if (depth == 1)
          parse(blocks :+ (currentBlock + curChar), "", remaining.tail, depth - 1)
        else
          parse(blocks, currentBlock + curChar, remaining.tail, depth - 1)
      case _ =>
        parse(blocks, currentBlock + curChar, remaining.tail, depth)
    }
  }

它产生与原始解决方案完全相同的输出。

要解决空块问题,我们需要更改 case '|':

case '|' if depth == 0 =>
  val updatedBlocks = if (currentBlock.isEmpty) blocks
                      else blocks :+ currentBlock
  parse(updatedBlocks, "", remaining.tail, depth)

如果当前块包含空字符串,我们将跳过它。


问题二

要合并 ; 字符之间的两个块,我们需要带回一个已解析的块并将其 return 放入 currentBlock 引用中。这表示另一种情况:

case ';' =>
  parse(blocks.init, blocks.last + curChar, remaining.tail, depth)

现在,之后

val result = parse(Seq(), "", s, 0)
result.foreach(println)

输出为

Str
[ts1:tssub2|ts1:tssub2]
BLANK
[INT1|X.X.X.X|INT2|BLANK |BLANK | |X.X.X.X|[INT3|s1]]
[INT3|INT4|INT5|INT6|INT7|INT8|INT9|INT10|INT11|INT12|INT13|INT14|INT15]
BLANK 
BLANK 
[s2|s3|s4|INT16|INT17];[s5|s6|s7|INT18|INT19]
[[s8|s9|s10|INT20|INT21]|ts3:tssub3| | ];[[s11|s12|s13|INT21|INT22]|INT23:INT24|BLANK |BLANK ]
BLANK 
BLANK 
[s14|s15]

它看起来与您正在寻找的非常相似。