2011-05-06 93 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