Comment puis-je imprimer des tables ASCII avec Python? [fermé]
je cherche une bibliothèque Python pour imprimer des tables comme celle-ci:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
j'ai trouvé asciitable mais il ne fait pas de frontières, etc. Je n'ai pas besoin de formatage complexe d'éléments de données, ce sont juste des chaînes. J'en ai besoin pour auto-dimensionner les colonnes.
est-ce qu'une telle chose existe, ou dois-je passer quelques minutes à écrire la mienne?
13 réponses
Voici une petite fonction rapide et sale que j'ai écrite pour afficher les résultats des requêtes SQL que je ne peux faire que sur une API SOAP. Il attend une entrée d'une séquence d'un ou plusieurs namedtuples
comme lignes de tableau. Si il n'y a qu'un seul enregistrement, qu'il imprime différemment.
C'est pratique pour moi et pourrait être un point de départ pour vous:
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])
sortie de L'échantillon:
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 | 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 | 1 132aa774-42e5-3d3f-498b-50b44a89d401 | 44e31f89-d089-8afc-f4b1-ada051c01474 | 1 ff91641a-5802-be02-bece-79bca993fdbc | 33d8294a-053d-6ab4-94d4-890b47fcf70d | 1 f3196e15-5b61-e92d-e717-f00ed93fe8ae | 62fa4566-5ca2-4a36-f872-4d00f7abadcf | 1
exemple
>>> 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
j'ai lu cette question il y a longtemps, et j'ai fini d'écrire ma propre jolie imprimante pour les tables: tabulate
.
mon cas d'utilisation est:
- je veux un one-liner, la plupart du temps
- qui est assez intelligent pour figurer le meilleur formatage pour moi
- et peut produire différents formats de texte en clair
vu votre exemple, grid
est probablement le format de sortie le plus similaire:
from tabulate import tabulate
print tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid")
+------------+------------+
| column 1 | column 2 |
+============+============+
| value1 | value2 |
+------------+------------+
| value3 | value4 |
+------------+------------+
autres formats pris en charge sont plain
(pas de lignes), simple
(Pandoc tables simples), pipe
(comme les tables en PHP Markdown Extra), orgtbl
(comme les tables en Emacs' org-mode), rst
(comme les tables simples en texte restructuré). grid
et orgtbl
sont facilement modifiables dans Emacs.
Performance sage, tabulate
est légèrement plus lent que asciitable
, mais beaucoup plus rapide que PrettyTable
et texttable
.
P.S. Je suis aussi un grand fan de l'alignement des nombres par une colonne décimale . Donc c'est l'alignement par défaut pour les nombres s'il y en a (overridable).
moi aussi j'ai écrit ma propre solution. J'ai essayé de garder les choses simples.
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] += '\nnewline'
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
+--------------+--------------+
| row1 column1 | row1 column2 |
| | newline |
+--------------+--------------+
| row2 column1 | row2 column2 |
+--------------+--------------+
Version utilisant w3m conçu pour manipuler les types que la version de MattH accepte:
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])
résultats dans:
┌───────┬─┐
│ first │1│
├───────┼─┤
│second │2│
├───────┼─┤
│ third │3│
└───────┴─┘
┌─────┬───────┬─────┐
│first│second │third│
├─────┼───────┼─────┤
│1 │2 │3 │
├─────┼───────┼─────┤
│4 │5 │6 │
└─────┴───────┴─────┘
si vous voulez une table avec des portées de colonne et de rangée, puis essayer Ma bibliothèque 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\nspan rows.", "- Cells\n- contain\n- 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))
qui produit:
+----------+------------+----------+----------+
| 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 |
+----------+------------+---------------------+
je sais que la question est un peu vieille mais voici ma tentative à ceci:
https://gist.github.com/lonetwin/4721748
C'est un peu plus lisible IMHO (bien qu'il ne fasse pas de différence entre les lignes simples / multiples comme le fait @Matth's solutions, et qu'il n'utilise pas non plus NamedTuples).
j'utilise cette petite fonction utilitaire.
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) + '\n'
output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '\n'
output += '-' * (sum(max_len) + 1) + '\n'
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)]) + '\n'
output += '-' * (sum(max_len) + 1) + '\n'
return output
print get_pretty_table([[1, 2], [3, 4]], ['header 1', 'header 2'])
sortie
-----------------
|header 1|header 2|
-----------------
|1 |2 |
|3 |4 |
-----------------
Vous pouvez essayer BeautifulTable . Il fait ce que vous voulez faire. Voici un exemple tiré de son documentation
>>> from beautifultable import BeautifulTable
>>> table = BeautifulTable()
>>> table.column_headers = ["name", "rank", "gender"]
>>> table.append_row(["Jacob", 1, "boy"])
>>> table.append_row(["Isabella", 1, "girl"])
>>> table.append_row(["Ethan", 2, "boy"])
>>> table.append_row(["Sophia", 2, "girl"])
>>> table.append_row(["Michael", 3, "boy"])
>>> print(table)
+----------+------+--------+
| name | rank | gender |
+----------+------+--------+
| Jacob | 1 | boy |
+----------+------+--------+
| Isabella | 1 | girl |
+----------+------+--------+
| Ethan | 2 | boy |
+----------+------+--------+
| Sophia | 2 | girl |
+----------+------+--------+
| Michael | 3 | boy |
+----------+------+--------+
voici ma solution:
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 "\n".join([header, sep] + rows)
cela peut être fait avec seulement des modules de construction assez compactes en utilisant des compréhensions de listes et de chaînes. Accepte une liste de dictionnaires de même format...
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 += "\n"
outmsg = lenstr.format(*dictlist[0].keys())
outmsg += "-" * (sum(lengths) + 3*len(lengths))
outmsg += "\n"
outmsg += "".join(
lenstr.format(*v) for v in [ item.values() for item in dictlist ]
)
return outmsg
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('\n' + rc_separator + '\n')
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('\n' + rc_separator + '\n')
else:
stdout.flush()
stdout.write('\n')
newline_indicator = 0
stdout.write(line)
newline_indicator += 1
stdout.write('\n' + rc_separator + '\n')
stdout.flush()
exemple:
table = {
"uid": ["0", "1", "2", "3"],
"name": ["Jon", "Doe", "Lemma", "Hemma"]
}
create_table(table)
sortie:
uid | name |
------+------------+-
0 | Jon |
1 | Doe |
2 | Lemma |
3 | Hemma |
------+------------+-
je viens de sortir asciiplotlib , et il a de jolies tables, aussi. Par exemple, ce
import asciiplotlib as apl
data = [
[["a", "bb", "ccc"]],
[[1, 2, 3], [613.23236243236, 613.23236243236, 613.23236243236]],
]
fig = apl.figure()
fig.table(data, border_style="thin", ascii_mode=True, padding=(0, 1), alignment="lcr")
fig.show()
vous permet de vous
+-----------------+-----------------+-----------------+
| a | bb | ccc |
+=================+=================+=================+
| 1 | 2 | 3 |
+-----------------+-----------------+-----------------+
| 613.23236243236 | 613.23236243236 | 613.23236243236 |
+-----------------+-----------------+-----------------+
Par défaut, la table est rendu avec Unicode zone de dessin de caractères ,
┌─────────────────┬─────────────────┬─────────────────┐
│ a │ bb │ ccc │
╞═════════════════╪═════════════════╪═════════════════╡
│ 1 │ 2 │ 3 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │
└─────────────────┴─────────────────┴─────────────────┘
les tables d'apl sont très configurables; consultez les tests pour plus d'exemples.