Как я могу красиво печатать таблицы ASCII с Python? [закрытый]
Я ищу библиотеку Python для печати таблиц, как это:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
Я нашел asciitable но это не делает границ и т. д. Мне не нужно никакого сложного форматирования элементов данных, они просто строки. Мне это нужно для автоматического размера столбцов.
существует ли такая вещь, или мне нужно потратить несколько минут, чтобы написать свой собственный?
13 ответов:
вот быстрая и грязная маленькая функция, которую я написал для отображения результатов SQL-запросов, которые я могу сделать только через SOAP API. Он ожидает ввода последовательности из одного или более
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 | 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
пример
>>> 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
я давно читал этот вопрос и закончил писать свой собственный симпатичный принтер для таблиц:
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),rst
(как простые таблицы в reStructuredText).grid
иorgtbl
легко редактируются в Emacs.производительность-мудрый,
tabulate
немного медленнее, чемasciitable
, но гораздо быстрее, чемPrettyTable
иtexttable
.P. S. Я тоже большой поклонник выравнивая цифры по десятичному столбцу. Таким образом, это выравнивание по умолчанию для чисел, если они есть (переопределяемые).
по какой-то причине, когда я включил "docutils" в мои поиски google, я наткнулся на texttable, который, кажется, то, что я ищу.
Я тоже написал свое собственное решение для этого. Я старался, чтобы все было просто.
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 | +--------------+--------------+
версия с использованием 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 │ └─────┴───────┴─────┘
Если вы хотите таблицу с диапазонами столбцов и строк, то попробуйте мою библиотеку 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))
выходы:
+----------+------------+----------+----------+ | 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 | +----------+------------+---------------------+
Я знаю, что вопрос немного старый, но вот моя попытка:
https://gist.github.com/lonetwin/4721748
Это немного более читаемый IMHO (хотя он не различает одиночные / множественные строки, как решения @MattH, и не использует NamedTuples).
Я использую эту небольшую функцию полезности.
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'])
выход
----------------- |header 1|header 2| ----------------- |1 |2 | |3 |4 | -----------------
вы можете попробовать BeautifulTable. Он делает то, что вы хотите сделать. Вот пример из него документация
>>> 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 | +----------+------+--------+
вот мое решение:
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)
Это можно сделать только со встроенными модулями довольно компактно, используя список и строковые понимания. Принимает список словарей одного формата...
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()
пример:
table = { "uid": ["0", "1", "2", "3"], "name": ["Jon", "Doe", "Lemma", "Hemma"] } create_table(table)
выход:
uid | name | ------+------------+- 0 | Jon | 1 | Doe | 2 | Lemma | 3 | Hemma | ------+------------+-
Я только что освободил asciiplotlib, и у него есть красивые столы, тоже. Например, это
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()
получает
+-----------------+-----------------+-----------------+ | a | bb | ccc | +=================+=================+=================+ | 1 | 2 | 3 | +-----------------+-----------------+-----------------+ | 613.23236243236 | 613.23236243236 | 613.23236243236 | +-----------------+-----------------+-----------------+
по умолчанию таблица отображается в Юникоде box-рисование символов,
┌─────────────────┬─────────────────┬─────────────────┐ │ a │ bb │ ccc │ ╞═════════════════╪═════════════════╪═════════════════╡ │ 1 │ 2 │ 3 │ ├─────────────────┼─────────────────┼─────────────────┤ │ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │ └─────────────────┴─────────────────┴─────────────────┘
таблицы apl очень настраиваются; проверьте тесты для получения дополнительных примеров.