2011-05-06 94 views
45

我在找一个Python库用于打印的表是这样的:如何用Python打印ASCII表格?

======================= 
| column 1 | column 2 | 
======================= 
| value1 | value2 | 
| value3 | value4 | 
======================= 

我发现asciitable,但它不会做边界等我不需要数据项的任何复杂的格式,他们只是字符串。我确实需要它来自动调整列的大小。

这样的事情是否存在,还是我需要花几分钟写自己的?

+0

为什么不使用docutils的为你做这个? – 2011-05-06 10:19:44

+0

你叫什么桌子?数据如何在表格中组织?值1,值2,值3,值4 ...列表中的连续值?我认为fomat()足以获得如此简单的显示效果,无需在很长时间内学习如何通过使用库来获取时间的教程 – eyquem 2011-05-06 10:40:51

+2

@korona:不,我没有提出建议。我在问一个问题。我不知道@kdt知道或不知道的。我不觉得,我不得不问。 – 2011-05-06 11:27:53

回答

15

出于某种原因,当我在Google搜索中添加了“docutils” hes我偶然发现了texttable,这似乎是我正在寻找的。

+1

不错的。缺少自动列宽检测;使用:http://pastebin.com/SAsPJUxM – Kos 2012-11-02 11:57:08

34

下面是我写的一个快速而脏的小函数,用于显示我只能通过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 | 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 
+0

@MattH你能用一个例子来展示这个函数的用法吗? – theAlse 2012-09-12 14:48:37

+1

@MattH谢谢,但大数似乎马上崩溃。 TypeError:类型'int'的对象没有len()。 – theAlse 2012-09-12 18:06:26

+0

@Alborz:我发布了这个作为其他人的出发点,如果你愿意,可以定制它来处理你的数据类型。尽管取决于错误来自哪条线,但您可能没有按照预期调用函数 – MattH 2012-09-12 19:00:53

17

好老线程,但最好的我找到了这个是Prettytable ...有没有更好的?

+0

它有两个问题: a)它不处理颜色(虽然我不知道任何库),和 b)它不似乎支持自定义格式(至少以选择预定义的“配方”的形式)。 – 2013-02-15 10:00:26

4

版本使用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 │ 
└─────┴───────┴─────┘ 
2

我知道它的问题是有点老,但这里是我尝试在此:

https://gist.github.com/lonetwin/4721748

它有点可读性恕我直言(尽管它不像@ MattH的解决方案那样区分单行/多行,也不使用NamedTuples)。

29

很久以前,我已经读过这个问题,并且完成了为自己的桌子编写漂亮的打印机: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降价额外的表),orgtbl(如在Emacs的组织模式表),rst(如在reStructuredText的简单的表格)。 Emacs中可以轻松编辑gridorgtbl

性能方面,tabulateasciitable稍慢,但比PrettyTabletexttable快得多。

P.S.我也是对齐号码by a decimal column的忠实粉丝。所以这是数字的默认对齐方式,如果有(可重写)。

+0

我只是碰巧需要一个制表解决方案,很幸运找到你的图书馆!工作就像一个魅力:D 如果你正在听,只想说**谢谢** :) :) – deepak 2016-10-06 05:02:08

+0

是的,我在听。谢谢你的客气话。获得积极的反馈非常好。 – sastanin 2016-10-17 09:54:11

+0

嗨,@sastanin首先,非常感谢你为这样一个不错的图书馆。我可否知道是否有任何选项可以打印表格以跨越终端的整个宽度? – 2016-10-30 06:40:59

10

我也写了我自己的解决方案。我试图保持简单。

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 | 
+--------------+--------------+ 
0

这里是我的解决方案:

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) 
2

我用这个小工具功能。

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  | 
----------------- 
+0

您在 'output + ='|'中添加每列之间的空格+''.join([h +''*(l - len(h))+'|'为h,l in zip(header,max_len)])+'\ n'' 但不在分隔符线。 可以通过一些简单的东西来扩展那一行'-'s 'output =' - '*(sum(max_len)+ 1 + len(header))+'\ n'' – ochawkeye 2016-11-08 17:27:29

2

如果你想与列和行跨度表,然后尝试我的图书馆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   | 
+----------+------------+---------------------+ 
1

你可以请尝试BeautifulTable。它做你想做的事。这里有一个例子,从它的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 | 
+----------+------+--------+ 
0

这只能与内置模块相当紧凑使用列表和字符串内涵来完成。接受所有相同格式的字典列表...

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 
0
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  | 
------+------------+- 
+0

您可以改进您的代码只能通过增加一些解释来回答。 – Yunnosch 2017-09-01 19:33:39