在功能方法中拆分复杂的字符串模式(没有正则表达式)

时间:2018-04-11 18:28:33

标签: scala parsing string-parsing idiomatic purely-functional

我试图在没有正则表达式的情况下使用更惯用的函数方法来分割字符串。

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]之后的额外条目/空格是如何来的,也是在

之后

[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 个答案:

答案 0 :(得分:1)

问题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)

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

问题2

要合并; char之间的两个块,我们需要返回一个已解析的块并将其返回到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]

它看起来与你想要的非常相似。