给定输入:
apple: banana eggplant
banana: cantaloupe durian
eggplant:
fig:
我想将其连接成格式:
├─ apple
│ ├─ banana
│ │ ├─ cantaloupe
│ │ └─ durian
│ └─ eggplant
└─ fig
可能有也可能没有多个根元素(在上面的示例中,有两个根元素),我想找到一个解决方案来处理它们而没有问题。
是否有任何命令行工具可以处理这种转换?如果不这样,其他脚本语言中是否有任何东西可以轻松地处理这个问题(我已经看过Python的pprint
但是我不确定如何将它用于这样的东西)?
答案 0 :(得分:3)
以下代码将生成您要求的树结构:
branch = '├'
pipe = '|'
end = '└'
dash = '─'
class Tree(object):
def __init__(self, tag):
self.tag = tag
class Node(Tree):
def __init__(self, tag, *nodes):
super(Node, self).__init__(tag)
self.nodes = list(nodes)
class Leaf(Tree):
pass
def _draw_tree(tree, level, last=False, sup=[]):
def update(left, i):
if i < len(left):
left[i] = ' '
return left
print ''.join(reduce(update, sup, ['{} '.format(pipe)] * level)) \
+ (end if last else branch) + '{} '.format(dash) \
+ str(tree.tag)
if isinstance(tree, Node):
level += 1
for node in tree.nodes[:-1]:
_draw_tree(node, level, sup=sup)
_draw_tree(tree.nodes[-1], level, True, [level] + sup)
def draw_tree(trees):
for tree in trees[:-1]:
_draw_tree(tree, 0)
_draw_tree(trees[-1], 0, True, [0])
它要求您使用给定的表单来表示数据。
关于你的数据反序列化,你只需要跟踪父节点,这样当一个叶子看起来像一个节点时,你只需要替换它:
class Track(object):
def __init__(self, parent, idx):
self.parent, self.idx = parent, idx
def parser(text):
trees = []
tracks = {}
for line in text.splitlines():
line = line.strip()
key, value = map(lambda s: s.strip(), line.split(':', 1))
nodes = value.split()
if len(nodes):
parent = Node(key)
for i, node in enumerate(nodes):
tracks[node] = Track(parent, i)
parent.nodes.append(Leaf(node))
curnode = parent
if curnode.tag in tracks:
t = tracks[curnode.tag]
t.parent.nodes[t.idx] = curnode
else:
trees.append(curnode)
else:
curnode = Leaf(key)
if curnode.tag in tracks:
# well, how you want to handle it?
pass # ignore
else:
trees.append(curnode)
return trees
它运行:
>>> text='''apple: banana eggplant
banana: cantaloupe durian
eggplant:
fig:'''
>>> draw_tree(parser(text))
├─ apple
| ├─ banana
| | ├─ cantaloupe
| | └─ durian
| └─ eggplant
└─ fig
希望它完全解决您的问题。
我的代码对极端情况提出了一些担忧,例如:
>>> text='''apple: banana eggplant
banana: cantaloupe durian
eggplant:'''
>>> draw_tree(parser(text))
└─ apple
├─ banana
| ├─ cantaloupe
| └─ durian
└─ eggplant
注意apple
子节点的最左侧,最后没有|
因为它们被抑制了。
或在中间空着:
>>> text='''apple: banana
banana: cantaloupe durian
eggplant:'''
>>> draw_tree(parser(text))
├─ apple
| └─ banana
| ├─ cantaloupe
| └─ durian
└─ eggplant
答案 1 :(得分:1)
这是另一个版本供您参考。
我还从stdin添加了读取文件,因此能够与bash脚本集成。您需要取消注释最后一部分。假设此脚本名为script.py,您可以调用python script.py < test.txt
来读取文件。 text.txt将存储您提供的文本内容。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
input_str = """apple: banana eggplant
banana: cantaloupe durian
eggplant:
fig:
"""
leaf_end_str = '└─ '
leaf_inner_str = '├─ '
child_conn_str = '│ '
empty_str = ' '
#debug = True
debug = False
def recursive_print(cur_root, p2r_list, prefix, is_end=False, only_one=False):
# first print current node
if only_one or is_end:
print '%s%s%s'%(prefix, leaf_end_str, cur_root)
else:
print '%s%s%s'%(prefix, leaf_inner_str, cur_root)
if only_one == True:
next_prefix = prefix + empty_str
else:
next_prefix = prefix + child_conn_str
#print p2r_list[cur_root]
if p2r_list.has_key(cur_root):
next_only_one = ( len(p2r_list[cur_root]) == 1 )
for child in p2r_list[cur_root]:
next_is_end = (child == p2r_list[cur_root][-1] )
recursive_print(child, p2r_list, next_prefix, is_end = next_is_end, only_one = next_only_one)
def tree_print(content):
# get root and parent-children relation
root = {} # check whether a node is root
p2r_list = {} # store the parent-child relation
for line in content.split('\n'):
line = line.strip()
if line == "":
continue
ws = line.split(':') # separate parent and child
if not root.has_key(ws[0]):
root[ws[0]] = True
if not p2r_list.has_key(ws[0]):
p2r_list[ws[0]] = []
if len(ws) > 1:
for child in ws[1].strip().split(' '):
if child == '':
continue
root[child] = False
p2r_list[ws[0]].append(child)
if debug:
print root, '\n', p2r_list
root_list = [r for r in root.keys() if root[r]]
for r in root_list:
if r == root_list[-1]:
recursive_print(r, p2r_list, '', is_end = True, only_one=True)
else:
recursive_print(r, p2r_list,'')
if __name__ == "__main__":
tree_print(input_str )
"""
content = sys.stdin.read()
#print content
if content != '':
tree_print( content)
#"""
答案 2 :(得分:1)
这个问题很老,但这是第一个解决方案的networkx版本:
def nx_ascii_tree(graph, key=None):
"""
Creates an printable ascii representation of a directed tree / forest.
Args:
graph (nx.DiGraph): each node has at most one parent (
i.e. graph must be a directed forest)
key (str): if specified, uses this node attribute as a label instead of
the id
References:
https://stackoverflow.com/questions/32151776/visualize-tree-in-bash-like-the-output-of-unix-tree
Example:
>>> import networkx as nx
>>> graph = nx.dfs_tree(nx.balanced_tree(2, 2), 0)
>>> text = nx_ascii_tree(graph)
>>> print(text)
└── 0
├── 1
│ ├── 3
│ └── 4
└── 2
├── 5
└── 6
"""
import six
import networkx as nx
branch = '├─'
pipe = '│'
end = '└─'
dash = '─'
assert nx.is_forest(graph)
assert nx.is_directed(graph)
lines = []
def _draw_tree_nx(graph, node, level, last=False, sup=[]):
def update(left, i):
if i < len(left):
left[i] = ' '
return left
initial = ['{} '.format(pipe)] * level
parts = six.moves.reduce(update, sup, initial)
prefix = ''.join(parts)
if key is None:
node_label = str(node)
else:
node_label = str(graph.nodes[node]['label'])
suffix = '{} '.format(dash) + node_label
if last:
line = prefix + end + suffix
else:
line = prefix + branch + suffix
lines.append(line)
children = list(graph.succ[node])
if children:
level += 1
for node in children[:-1]:
_draw_tree_nx(graph, node, level, sup=sup)
_draw_tree_nx(graph, children[-1], level, True, [level] + sup)
def draw_tree(graph):
source_nodes = [n for n in graph.nodes if graph.in_degree[n] == 0]
if source_nodes:
level = 0
for node in source_nodes[:-1]:
_draw_tree_nx(graph, node, level, last=False, sup=[])
_draw_tree_nx(graph, source_nodes[-1], level, last=True, sup=[0])
draw_tree(graph)
text = '\n'.join(lines)
return text