如何使用 Python 漂亮地打印 ASCII 表?[关闭]
- 2025-02-27 09:07:00
- admin 原创
- 59
问题描述:
我正在寻找一种方法来漂亮地打印表格,如下所示:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
我找到了asciitable库,但它不支持边框等。我不需要任何复杂的数据项格式,它们只是字符串。我确实需要它来自动调整列的大小。
是否存在其他库或方法,或者我是否需要花几分钟编写自己的库或方法?
解决方案 1:
我很久以前就读过这个问题,并且完成了我自己的表格漂亮打印机的编写:tabulate
。
我的用例是:
我大多数时候都想要一句俏皮话
它足够智能,可以为我找到最佳格式
并可以输出不同的纯文本格式
鉴于你的例子,grid
可能是最相似的输出格式:
from tabulate import tabulate
print(tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid"))
+------------+------------+
| column 1 | column 2 |
+============+============+
| value1 | value2 |
+------------+------------+
| value3 | value4 |
+------------+------------+
其他支持的格式包括plain
(无线)、simple
(Pandoc 简单表格)、pipe
(如 PHP Markdown Extra 中的表格)、orgtbl
(如 Emacs 的 org-mode 中的表格)、rst
(如 reStructuredText 中的简单表格)。grid
并且orgtbl
可以在 Emacs 中轻松编辑。
性能方面,tabulate
比 稍慢,但比和asciitable
快得多。PrettyTable
`texttable`
PS 我也非常喜欢按小数列对齐数字。因此,如果有数字,这是数字的默认对齐方式(可覆盖)。
解决方案 2:
这是我编写的一个简单而粗暴的小函数,用于显示只能通过 SOAP API 进行的 SQL 查询的结果。它需要输入一个或多个namedtuples
表行序列。如果只有一条记录,它会以不同的方式打印出来。
它对我来说很方便,并且可以作为您的起点:
def pprinttable(rows):
if len(rows) > 1:
headers = rows[0]._fields
lens = []
for i in range(len(rows[0])):
lens.append(len(max([x[i] for x in rows] + [headers[i]],key=lambda x:len(str(x)))))
formats = []
hformats = []
for i in range(len(rows[0])):
if isinstance(rows[0][i], int):
formats.append("%%%dd" % lens[i])
else:
formats.append("%%-%ds" % lens[i])
hformats.append("%%-%ds" % lens[i])
pattern = " | ".join(formats)
hpattern = " | ".join(hformats)
separator = "-+-".join(['-' * n for n in lens])
print hpattern % tuple(headers)
print separator
_u = lambda t: t.decode('UTF-8', 'replace') if isinstance(t, str) else t
for line in rows:
print pattern % tuple(_u(t) for t in line)
elif len(rows) == 1:
row = rows[0]
hwidth = len(max(row._fields,key=lambda x: len(x)))
for i in range(len(row)):
print "%*s = %s" % (hwidth,row._fields[i],row[i])
示例输出:
pkid | fkn | npi
------------------------------------------------+------------------------------------------------+----
405fd665-0a2f-4f69-7320-be01201752ec | 8c9949b9-552e-e448-64e2-74292834c73e | 0
5b517507-2a42-ad2e-98dc-8c9ac6152afa | f972bee7-f5a4-8532-c4e5-2e82897b10f6 | 0
2f960dfc-b67a-26be-d1b3-9b105535e0a8 | ec3e1058-8840-c9f2-3b25-2488f8b3a8af | 1
c71b28a3-5299-7f4d-f27a-7ad8aeadafe0 | c71b28a3-5299-7f4d-f27a-7ad8aeadafe0 72d25703-4735-310b-2e06-ff76af1e45ed| 0
3b0a5021-a52b-9ba0-1439-d5aafcf348e7 | d81bb78a-d984-e957-034d-87434acb4e97 | 1
96c36bb7-c4f4-2787-ada8-4aadc17d1123 | c171fe85-33e2-6481-0791-2922267e8777 | 1
95d0f85f-71da-bb9a-2d80-fe27f7c02fe2 | 226f964c-028d-d6de-bf6c-688d2908c5ae | 226f964c-028d-d6de-bf6c-688d2908c5ae 1
132aa774-42e5-3d3f-498b-50b44a89d401 | 44e31f89-d089-8afc-f4b1-ada051c01474 | 1
ff91641a-5802-be02-bece-79bca993fdbc | ff91641a-5802-be02-bece-79bca993fdbc 33d8294a-053d-6ab4-94d4-890b47fcf70d| 1
f3196e15-5b61-e92d-e717-f00ed93fe8ae | 62fa4566-5ca2-4a36-f872-4d00f7abadcf | 1
例子
>>> from collections import namedtuple
>>> Row = namedtuple('Row',['first','second','third'])
>>> data = Row(1,2,3)
>>> data
Row(first=1, second=2, third=3)
>>> pprinttable([data])
first = 1
second = 2
third = 3
>>> pprinttable([data,data])
first | second | third
------+--------+------
1 | 2 | 3
1 | 2 | 3
解决方案 3:
出于某种原因,当我在谷歌搜索中包含“docutils”时,我偶然发现了texttable,这似乎就是我正在寻找的东西。
解决方案 4:
我也为此写了自己的解决方案。我尽量让它保持简单。
https://github.com/Robpol86/terminaltables
from terminaltables import AsciiTable
table_data = [
['Heading1', 'Heading2'],
['row1 column1', 'row1 column2'],
['row2 column1', 'row2 column2']
]
table = AsciiTable(table_data)
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
+--------------+--------------+
| row1 column1 | row1 column2 |
| row2 column1 | row2 column2 |
+--------------+--------------+
table.inner_heading_row_border = False
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
| row1 column1 | row1 column2 |
| row2 column1 | row2 column2 |
+--------------+--------------+
table.inner_row_border = True
table.justify_columns[1] = 'right'
table.table_data[1][1] += '
newline'
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
+--------------+--------------+
| row1 column1 | row1 column2 |
| | newline |
+--------------+--------------+
| row2 column1 | row2 column2 |
+--------------+--------------+
解决方案 5:
我刚刚发布了用于此目的的术语表。例如,这个
import termtables as tt
tt.print(
[[1, 2, 3], [613.23236243236, 613.23236243236, 613.23236243236]],
header=["a", "bb", "ccc"],
style=tt.styles.ascii_thin_double,
padding=(0, 1),
alignment="lcr"
)
让你
+-----------------+-----------------+-----------------+
| a | bb | ccc |
+=================+=================+=================+
| 1 | 2 | 3 |
+-----------------+-----------------+-----------------+
| 613.23236243236 | 613.23236243236 | 613.23236243236 |
+-----------------+-----------------+-----------------+
默认情况下,表格使用 Unicode方框绘制字符呈现,
┌─────────────────┬─────────────────┬─────────────────┐
│ a │ bb │ ccc │
╞═════════════════╪═════════════════╪═════════════════╡
│ 1 │ 2 │ 3 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │
└─────────────────┴─────────────────┴─────────────────┘
termtables 非常易于配置;查看测试以获取更多示例。
解决方案 6:
如果你想要一个有列和行跨度的表格,那么试试我的库dashtable
from dashtable import data2rst
table = [
["Header 1", "Header 2", "Header3", "Header 4"],
["row 1", "column 2", "column 3", "column 4"],
["row 2", "Cells span columns.", "", ""],
["row 3", "Cells
span rows.", "- Cells
- contain
- blocks", ""],
["row 4", "", "", ""]
]
# [Row, Column] pairs of merged cells
span0 = ([2, 1], [2, 2], [2, 3])
span1 = ([3, 1], [4, 1])
span2 = ([3, 3], [3, 2], [4, 2], [4, 3])
my_spans = [span0, span1, span2]
print(data2rst(table, spans=my_spans, use_headers=True))
输出:
+----------+------------+----------+----------+
| Header 1 | Header 2 | Header3 | Header 4 |
+==========+============+==========+==========+
| row 1 | column 2 | column 3 | column 4 |
+----------+------------+----------+----------+
| row 2 | Cells span columns. |
+----------+----------------------------------+
| row 3 | Cells | - Cells |
+----------+ span rows. | - contain |
| row 4 | | - blocks |
+----------+------------+---------------------+
解决方案 7:
使用 w3m 的版本旨在处理 MattH 版本接受的类型:
import subprocess
import tempfile
import html
def pprinttable(rows):
esc = lambda x: html.escape(str(x))
sour = "<table border=1>"
if len(rows) == 1:
for i in range(len(rows[0]._fields)):
sour += "<tr><th>%s<td>%s" % (esc(rows[0]._fields[i]), esc(rows[0][i]))
else:
sour += "<tr>" + "".join(["<th>%s" % esc(x) for x in rows[0]._fields])
sour += "".join(["<tr>%s" % "".join(["<td>%s" % esc(y) for y in x]) for x in rows])
with tempfile.NamedTemporaryFile(suffix=".html") as f:
f.write(sour.encode("utf-8"))
f.flush()
print(
subprocess
.Popen(["w3m","-dump",f.name], stdout=subprocess.PIPE)
.communicate()[0].decode("utf-8").strip()
)
from collections import namedtuple
Row = namedtuple('Row',['first','second','third'])
data1 = Row(1,2,3)
data2 = Row(4,5,6)
pprinttable([data1])
pprinttable([data1,data2])
结果:
┌───────┬─┐
│ first │1│
├───────┼─┤
│second │2│
├───────┼─┤
│ third │3│
└───────┴─┘
┌─────┬───────┬─────┐
│first│second │third│
├─────┼───────┼─────┤
│1 │2 │3 │
├─────┼───────┼─────┤
│4 │5 │6 │
└─────┴───────┴─────┘
解决方案 8:
您可以尝试BeautifulTable。它可以完成您想要的操作。以下是其文档中的一个例子
>>> from beautifultable import BeautifulTable
>>> table = BeautifulTable()
>>> table.columns.header = ["name", "rank", "gender"]
>>> table.rows.append(["Jacob", 1, "boy"])
>>> table.rows.append(["Isabella", 1, "girl"])
>>> table.rows.append(["Ethan", 2, "boy"])
>>> table.rows.append(["Sophia", 2, "girl"])
>>> table.rows.append(["Michael", 3, "boy"])
>>> print(table)
+----------+------+--------+
| name | rank | gender |
+----------+------+--------+
| Jacob | 1 | boy |
+----------+------+--------+
| Isabella | 1 | girl |
+----------+------+--------+
| Ethan | 2 | boy |
+----------+------+--------+
| Sophia | 2 | girl |
+----------+------+--------+
| Michael | 3 | boy |
+----------+------+--------+
解决方案 9:
from sys import stderr, stdout
def create_table(table: dict, full_row: bool = False) -> None:
min_len = len(min((v for v in table.values()), key=lambda q: len(q)))
max_len = len(max((v for v in table.values()), key=lambda q: len(q)))
if min_len < max_len:
stderr.write("Table is out of shape, please make sure all columns have the same length.")
stderr.flush()
return
additional_spacing = 1
heading_separator = '| '
horizontal_split = '| '
rc_separator = ''
key_list = list(table.keys())
rc_len_values = []
for key in key_list:
rc_len = len(max((v for v in table[key]), key=lambda q: len(str(q))))
rc_len_values += ([rc_len, [key]] for n in range(len(table[key])))
heading_line = (key + (" " * (rc_len + (additional_spacing + 1)))) + heading_separator
stdout.write(heading_line)
rc_separator += ("-" * (len(key) + (rc_len + (additional_spacing + 1)))) + '+-'
if key is key_list[-1]:
stdout.flush()
stdout.write('
' + rc_separator + '
')
value_list = [v for vl in table.values() for v in vl]
aligned_data_offset = max_len
row_count = len(key_list)
next_idx = 0
newline_indicator = 0
iterations = 0
for n in range(len(value_list)):
key = rc_len_values[next_idx][1][0]
rc_len = rc_len_values[next_idx][0]
line = ('{:{}} ' + " " * len(key)).format(value_list[next_idx], str(rc_len + additional_spacing)) + horizontal_split
if next_idx >= (len(value_list) - aligned_data_offset):
next_idx = iterations + 1
iterations += 1
else:
next_idx += aligned_data_offset
if newline_indicator >= row_count:
if full_row:
stdout.flush()
stdout.write('
' + rc_separator + '
')
else:
stdout.flush()
stdout.write('
')
newline_indicator = 0
stdout.write(line)
newline_indicator += 1
stdout.write('
' + rc_separator + '
')
stdout.flush()
例子:
table = {
"uid": ["0", "1", "2", "3"],
"name": ["Jon", "Doe", "Lemma", "Hemma"]
}
create_table(table)
输出:
uid | name |
------+------------+-
0 | Jon |
1 | Doe |
2 | Lemma |
3 | Hemma |
------+------------+-
解决方案 10:
我知道这个问题有点老了但这是我的尝试:
https://gist.github.com/lonetwin/4721748
在我看来它更具可读性(尽管它不像@MattH 的解决方案那样区分单行/多行,也不使用 NamedTuples)。
解决方案 11:
这是我的解决方案:
def make_table(columns, data):
"""Create an ASCII table and return it as a string.
Pass a list of strings to use as columns in the table and a list of
dicts. The strings in 'columns' will be used as the keys to the dicts in
'data.'
Not all column values have to be present in each data dict.
>>> print(make_table(["a", "b"], [{"a": "1", "b": "test"}]))
| a | b |
|----------|
| 1 | test |
"""
# Calculate how wide each cell needs to be
cell_widths = {}
for c in columns:
values = [str(d.get(c, "")) for d in data]
cell_widths[c] = len(max(values + [c]))
# Used for formatting rows of data
row_template = "|" + " {} |" * len(columns)
# CONSTRUCT THE TABLE
# The top row with the column titles
justified_column_heads = [c.ljust(cell_widths[c]) for c in columns]
header = row_template.format(*justified_column_heads)
# The second row contains separators
sep = "|" + "-" * (len(header) - 2) + "|"
# Rows of data
rows = []
for d in data:
fields = [str(d.get(c, "")).ljust(cell_widths[c]) for c in columns]
row = row_template.format(*fields)
rows.append(row)
return "
".join([header, sep] + rows)
解决方案 12:
我使用这个小实用功能。
def get_pretty_table(iterable, header):
max_len = [len(x) for x in header]
for row in iterable:
row = [row] if type(row) not in (list, tuple) else row
for index, col in enumerate(row):
if max_len[index] < len(str(col)):
max_len[index] = len(str(col))
output = '-' * (sum(max_len) + 1) + '
'
output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '
'
output += '-' * (sum(max_len) + 1) + '
'
for row in iterable:
row = [row] if type(row) not in (list, tuple) else row
output += '|' + ''.join([str(c) + ' ' * (l - len(str(c))) + '|' for c, l in zip(row, max_len)]) + '
'
output += '-' * (sum(max_len) + 1) + '
'
return output
print get_pretty_table([[1, 2], [3, 4]], ['header 1', 'header 2'])
输出
-----------------
|header 1|header 2|
-----------------
|1 |2 |
|3 |4 |
-----------------
解决方案 13:
这可以仅使用内置模块以相当紧凑的方式使用列表和字符串理解来实现。接受所有格式相同的字典列表...
def tableit(dictlist):
lengths = [ max(map(lambda x:len(x.get(k)), dictlist) + [len(k)]) for k in dictlist[0].keys() ]
lenstr = " | ".join("{:<%s}" % m for m in lengths)
lenstr += "
"
outmsg = lenstr.format(*dictlist[0].keys())
outmsg += "-" * (sum(lengths) + 3*len(lengths))
outmsg += "
"
outmsg += "".join(
lenstr.format(*v) for v in [ item.values() for item in dictlist ]
)
return outmsg
扫码咨询,免费领取项目管理大礼包!