AWK递归树结构

AWK recursive tree structure

我正在尝试解析包含分层结构中的行的文件。例如文件:

a b c
a b d
a B C
A B C

表示a包含bB,表示b包含cd,表示B包含 CA 包含不同的 B,后者包含自己的 C

这很像一个文件列表。

我想以分层括号的方式对其进行格式化,例如:

a {
    b {
        c
        d
    }
    B {
        C
    }
}
A {
    B {
        C
    }
}

我想不出一个合适的方法来做到这一点。我认为 AWK 是我最好的选择,但我想不出如何实际实施它。

上下文

我的输入实际上是一个文件列表。如果需要,我当然可以用空格分隔字段,或者用 / 保留它们。这些文件是无序的,是在编译时通过检查从代码库生成的。我想要的输出将是一个 graphviz DOT 文件,其中包含每个文件在其自己的子图中。

因此对于输入:

a/b/c
a/b/d
a/B/C
A/B/C

输出将是

digraph {
  subgraph cluster_a {
    label = a
    subgraph cluster_b {
        label = b
        node_1 [label=c]
        node_2 [label=d]
    }
    subgraph cluster_B {
        label = B
        node_3 [label=C]
    }
  }
  subgraph cluster_A {
      label = A
      subgraph cluster_B {
          label = B
          node_4 [label=C]
      }
  }
}

有人知道我怎样才能完成这个处理吗?我也对其他工具持开放态度,而不仅仅是 AWK。

注意:深度不是固定的,但如有必要,我可以预先计算最大深度。也不是所有的叶子都处于相同的深度。

如果深度固定在3层

gawk -F/ '
    {f[][][] = 1}
    END {
        n = 0
        print "digraph {"
        for (a in f) {
            print "  subgraph cluster_" a " {"
            print "    label = " a
            for (b in f[a]) {
                print "    subgraph cluster_" b " {"
                print "      label = " b
                for (c in f[a][b]) {
                    printf "      node_%d [label=%s]\n", ++n, c
                }
                print "    }"
            }
            print "  }"
        }
        print "}"
    }
' file
digraph {
  subgraph cluster_A {
    label = A
    subgraph cluster_B {
      label = B
      node_1 [label=C]
    }
  }
  subgraph cluster_a {
    label = a
    subgraph cluster_B {
      label = B
      node_2 [label=C]
    }
    subgraph cluster_b {
      label = b
      node_3 [label=c]
      node_4 [label=d]
    }
  }
}

如果深度是任意的,事情就会变得复杂。

I'm open to other tools as well, not just AWK.

我提供这个Python解决方案:

import sys

INDENT = '  '
NODE_COUNT = 1


def build(node, l):
    x = l[0]
    if x not in node:
        node[x] = {}

    if len(l) > 1:
        build(node[x], l[1:])


def indent(s, depth):
    print('%s%s' % (INDENT * depth, s))


def print_node(label, value, depth):

    if len(value.keys()) > 0:
        indent('subgraph cluster_%s {' % label, depth)
        indent('  label = %s' % label, depth)
        for child in value:
            print_node(child, value[child], depth+1)
        indent('}', depth)
    else:
        global NODE_COUNT
        indent('node_%d [label=%s]' % (NODE_COUNT, label), depth)
        NODE_COUNT += 1


def main():

    d = {}

    for line in sys.stdin:
        build(d, [x.strip() for x in line.split()])

    print('digraph {')
    for k in d.keys():
        print_node(k, d[k], 1)
    print('}')


if __name__ == '__main__':
    main()

结果:

$ cat rels.txt
a b c
a b d
a B C
A B C

$ cat rels.txt | python3 make_rels.py
digraph {
  subgraph cluster_a {
    label = a
    subgraph cluster_b {
      label = b
      node_1 [label=c]
      node_2 [label=d]
    }
    subgraph cluster_B {
      label = B
      node_3 [label=C]
    }
  }
  subgraph cluster_A {
    label = A
    subgraph cluster_B {
      label = B
      node_4 [label=C]
    }
  }
}