Groovy - 具有多个括号匹配的拆分字符串

时间:2014-03-12 18:33:14

标签: regex groovy

我有一个以下字符串:

'(1:A&((2:B | 3:C)&(4:D | 5:E)))| (6:F& 7:G)'

我希望将它们解析为树层次结构对象。 1:A,2:B,3:C应该是叶子和&或者在根。 括号很重要,因此示例中的字符串应转换为:

            |
         /    \
        /      \
       &        &
      / \      / \
     /   \    /   \
   1:A    &  6:F  7:G
         / \
        /   \
       /     \      
      |      |
     / \    / \
  2:B 3:C  4:D 5:E

我试图使用拆分括号的模式,但我无法将结果转换为合理的层次结构。 我的想法用完了,也许你们中的一些人会有一些想法?

3 个答案:

答案 0 :(得分:1)

答案 1 :(得分:0)

认为这样做(至少对你的例子输入而言)。

首先,我将所有X:Y个实例替换为Leaf个实例,将Leaf存储在缓存列表中,并将X:Y替换为缓存中Leaf的索引。

然后,我反复查找(L O R)模式,为它创建一个Node实例(在缓存中查找L和R),将其添加到缓存中,并将其替换为字符串中的缓存索引。

然后你留下一个字符串11 | 9(或类似的),可以像以前一样转换成一个节点。

希望能够解释它。如果这是针对任何严重的问题,或者将来需要扩展,那么使用正确的解析器生成器可能是一个更明智的解决方案......

import groovy.transform.*

def input = '(1:A & ((2:B | 3:C) & (4:D | 5:E))) | (6:F & 7:G)'

def cache = []

// Remove all X:Y items into a lookup table
input = input.replaceAll( /\w+:\w+/ ) {
  cache << new Leaf( it )
  cache.size() - 1
}

// Then keep finding Nodes filling them with cached values and adding them into the cache
while( input ==~ /.*\(\d+ [&|] \d+\).*/ ) {
    input = input.replaceAll( /\(\d+ [&|] \d+\)/ ) {
        def (left,op,right) = it[ 1..-2 ].tokenize()
        cache << new Node( cache[ left.toInteger() ], op, cache[ right.toInteger() ] )
        cache.size() - 1
    }
}

// Then we're left with "11 | 9", so make the root node
def (left,op,right) = input.tokenize()
def tree = new Node( cache[ left.toInteger() ], op, cache[ right.toInteger() ] )

println tree

///////////////////////////////////////////////////////
// Classes to hold leafs and nodes

@Canonical
class Leaf {
    String value
    String toString( String prefix ) {
        "${prefix}${value}"
    }
}

@TupleConstructor
class Node {
    def left, op, right
    String toString( String prefix ) {
        """${prefix}left:
          |${prefix}${left.toString( prefix + '    ' )}
          |${prefix}op: $op
          |${prefix}right:
          |${prefix}${right.toString( prefix + '    ' )}""".stripMargin()
    }
    String toString() {
        toString( '' )
    }
}

答案 2 :(得分:0)

使用正则表达式的替代解决方案。

这个想法是在paranthesis中递归地解析匹配叶节点和表达式的输入字符串。 parse方法的每个invokation都将匹配它的整个输入,并返回一个带有输入值的节点(如果它是一个叶子)或一个带有两个子节点的节点,这两个子节点是递归计算的(如果它匹配一个模式的“表达式运算符表达式”)。

目前的形式需要高度定期的输入:

  • 一个叶子必须在数字形式:upperCaseCharacter,没有括号
  • 运算符和表达式之间的一个空格
  • 表达式是叶子或括号中包含的内容

每当正则表达式匹配时,将收集所有匹配的组并删除空值。将有8个非空组(因为分组括号的数量始终相同),内部组将始终为数字2,3和5,其中3是运算符,2和5是左和右的输入分别是右节点。

toString方法只是用波兰表示法写出表达式(op ex1 ex2)。

class Node {
    String value
    Node left, right

    static leaf = /(\d:[A-Z])/
    static op = /([&|])/
    static par = /\((.+)\)/

    static parse(String text) {
        if (text ==~ "^$leaf\$") {
            return new Node(value: text)
        }
        def matcher = text =~ "^($leaf|$par) $op ($leaf|$par)\$"
        if (matcher.matches()) {
            def (l,o,r) = (matcher[0] - null)[2,3,5]
            return new Node(left: parse(l), value: o, right: parse(r))
        }
        throw new IllegalArgumentException("Irregular input: $text")
    }

    String toString() {
        left && right ? "$value $left $right" : value
    }
}

示例:

println Node.parse('1:A')
println Node.parse('1:A & 2:B')
println Node.parse('(1:A & 2:B) | 3:C')
println Node.parse('(1:A & ((2:B | 3:C) & (4:D | 5:E))) | (6:F & 7:G)')

生成以下波兰表示法输出:

1:A
& 1:A 2:B
| & 1:A 2:B 3:C
| & 1:A & | 2:B 3:C | 4:D 5:E & 6:F 7:G