Làm cách nào để in các bảng ASCII bằng Python? [đóng cửa]


81

Tôi đang tìm một cách để in những bảng đẹp như thế này:

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

Tôi đã tìm thấy asciitable thư viện nhưng nó không làm biên giới, vv Tôi không cần bất kỳ định dạng phức tạp của các mục dữ liệu, họ chỉ là chuỗi đang. Tôi cần nó để tự động kích thước cột.

Các thư viện hoặc phương pháp khác có tồn tại không, hay tôi cần dành vài phút để viết riêng?


Tại sao không sử dụng docutils để làm điều này cho bạn?
S.Lott

Bạn gọi một cái bàn là gì? Dữ liệu được tổ chức trong một bảng như thế nào? Value1, value2, value3, value4 ... có phải là các giá trị kế tiếp nhau trong một danh sách không? Tôi nghĩ rằng fomat () là đủ để có được một màn hình đơn giản như vậy, không cần phải học trong một thời gian dài một hướng dẫn giải thích làm thế nào để đạt được thời gian bằng cách sử dụng một thư viện
Eyquem

2
@korona: Không, tôi không đưa ra đề xuất. Tôi đã hỏi một câu hỏi. Tôi không có manh mối về những gì @kdt biết hoặc không biết. Thay vì giả định, tôi cảm thấy buộc phải hỏi.
S.Lott

5
Tôi nghe như thể bạn thực sự đang giả định rằng anh ấy biết về những bộ tóc cắt tỉa. Có lẽ anh ta không?
korona

2
@ S.Lott Tôi đã xem các hình vẽ docutils, và tất nhiên là nó rất tuyệt để chuyển đổi văn bản sang html, latex, v.v., tôi không thấy cách nào để tạo bảng văn bản đẹp, với các cột thẳng hàng và trông đẹp mắt. phông chữ có chiều rộng cố định. Bạn đã hiểu sai mục tiêu của kdt, hay tôi đang thiếu cái gì đó?
nealmcb

Câu trả lời:


71

Tôi đã đọc câu hỏi này lâu lắm rồi, và viết xong của riêng tôi khá-in cho các bảng: tabulate.

Trường hợp sử dụng của tôi là:

  • Tôi muốn một lớp lót hầu hết thời gian
  • đủ thông minh để tìm ra định dạng tốt nhất cho tôi
  • và có thể xuất ra các định dạng văn bản thuần túy khác nhau

Với ví dụ của bạn, gridcó lẽ là định dạng đầu ra tương tự nhất:

from tabulate import tabulate
print tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid")
+------------+------------+
| column 1   | column 2   |
+============+============+
| value1     | value2     |
+------------+------------+
| value3     | value4     |
+------------+------------+

Các định dạng được hỗ trợ khác là plain(không có dòng), simple(bảng đơn giản Pandoc), pipe(như bảng trong PHP Markdown Extra), orgtbl(như bảng trong chế độ org của Emacs), rst(như bảng đơn giản trong reStructuredText). gridorgtblcó thể dễ dàng chỉnh sửa trong Emacs.

Hiệu suất khôn ngoan tabulatelà hơi chậm hơn asciitable, nhưng nhanh hơn nhiều so với PrettyTabletexttable.

Tái bút Tôi cũng là một fan hâm mộ lớn của việc sắp xếp các số theo cột thập phân . Vì vậy, đây là căn chỉnh mặc định cho các số nếu có (có thể ghi đè).


4
Tôi chỉ tình cờ cần một giải pháp lập bảng và đủ may mắn để tìm thấy thư viện của bạn! Công trình như một say mê: D Trong trường hợp bạn đang nghe, chỉ muốn nói cảm ơn :)
Deepak

2
Vâng, tôi đang nghe đây. Cảm ơn bạn cho các từ loại. Thật tuyệt khi nhận được phản hồi tích cực.
sastanin

1
Xin chào, @sastanin Trước hết, cảm ơn bạn rất nhiều vì một thư viện đẹp như vậy. Tôi có thể biết có tùy chọn nào để in bảng để mở rộng toàn bộ chiều rộng của thiết bị đầu cuối không?
Validus Oculus,

1
Xin chào sastanin, chỉ muốn gửi một lời ở đây để cảm ơn bạn vì gói rất tiện dụng này. Hoạt động như một cái duyên và giúp tôi tránh khỏi những rắc rối khi viết riêng. Cảm ơn rất nhiều vì đã chia sẻ!
Valentin B.

1
Danh sách tính năng của bạn là một cách nói nhỏ. Đã thử ansi thứ thoát, hoạt động hoàn hảo. Cám ơn vì cái này!
Red Pill

37

Đây là một hàm nhỏ nhanh và bẩn mà tôi đã viết để hiển thị kết quả từ các truy vấn SQL mà tôi chỉ có thể thực hiện qua API SOAP. Nó mong đợi một đầu vào của một chuỗi gồm một hoặc nhiều hàng namedtuplesdưới dạng bảng. Nếu chỉ có một bản ghi, nó sẽ in ra khác.

Nó hữu ích cho tôi và có thể là một điểm khởi đầu cho bạn:

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

Đầu ra mẫu:

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

Thí dụ

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

@MattH bạn có thể chỉ ra cách sử dụng của chức năng này với một ví dụ không?
theAlse 12/09/12

1
@MattH cảm ơn, nhưng số lượng lớn dường như làm hỏng nó ngay lập tức. TypeError: đối tượng kiểu 'int' không có len ().
theAlse 12/09/12

@Alborz: Tôi đã đăng điều này như một điểm khởi đầu cho những người khác, hãy tùy chỉnh nó để xử lý các loại dữ liệu của bạn nếu bạn muốn. Mặc dù tùy thuộc vào dòng lỗi đó đến từ đâu, bạn có thể không gọi hàm như dự định
MattH 12/09/12

1
@theAlse Tôi đã sửa lỗi bạn đã xác định bằng cách đưa nó len(str(max(...)))vào dòng lens.append. Vì vậy, bây giờ nếu một số trong cột rộng hơn tiêu đề cột, chúng ta vẫn tốt. BTW, MattH - cách sử dụng dễ thương đối số "key" thành max ()!
nealmcb

19

Vì một số lý do khi tôi đưa 'docutils' vào các tìm kiếm trên google của mình, tôi tình cờ bắt gặp bảng văn bản , có vẻ là thứ tôi đang tìm kiếm.


2
Tốt lắm. Thiếu phát hiện chiều rộng cột tự động; sử dụng: pastebin.com/SAsPJUxM
Kos

12

Tôi cũng đã viết giải pháp của riêng tôi cho điều này. Tôi đã cố gắng giữ cho nó đơn giản.

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

9

Tôi chỉ phát hành bảng kỳ hạn cho mục đích này. Ví dụ, điều này

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

có được bạn

+-----------------+-----------------+-----------------+
| a               |       bb        |             ccc |
+=================+=================+=================+
| 1               |        2        |               3 |
+-----------------+-----------------+-----------------+
| 613.23236243236 | 613.23236243236 | 613.23236243236 |
+-----------------+-----------------+-----------------+

Theo mặc định, bảng được hiển thị bằng các ký tự vẽ hộp Unicode ,

┌─────────────────┬─────────────────┬─────────────────┐
│ a               │       bb        │             ccc │
╞═════════════════╪═════════════════╪═════════════════╡
│ 123 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236613.23236243236613.23236243236 │
└─────────────────┴─────────────────┴─────────────────┘

bảng kỳ hạn rất dễ cấu hình; kiểm tra các bài kiểm tra để biết thêm ví dụ.


Tôi ước bạn có thể đặt cột tối đa để hiển thị và để thư viện xử lý logic gói.
Kang Min Yoo

7

Bạn có thể thử BeautifulTable . Nó làm những gì bạn muốn làm. Đây là một ví dụ từ tài liệu của nó

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

/usr/local/lib/python3.8/site-packages/beautifultable/utils.py:113: FutureWarning: 'BeautifulTable.column_headers' has been deprecated in 'v1.0.0' and will be removed in 'v1.2.0'. Use 'BTColumnCollection.header' instead. warnings.warn(message, FutureWarning)
evandrix

/usr/local/lib/python3.8/site-packages/beautifultable/utils.py:113: FutureWarning: 'BeautifulTable.append_row' has been deprecated in 'v1.0.0' and will be removed in 'v1.2.0'. Use 'BTRowCollection.append' instead. warnings.warn(message, FutureWarning)
evandrix

6

Phiên bản sử dụng w3m được thiết kế để xử lý các loại mà phiên bản của MattH chấp nhận:

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

kết quả trong:

┌───────┬─┐
│ first │1│
├───────┼─┤
│second │2│
├───────┼─┤
│ third │3│
└───────┴─┘
┌─────┬───────┬─────┐
│first│second │third│
├─────┼───────┼─────┤
│123    │
├─────┼───────┼─────┤
│456    │
└─────┴───────┴─────┘

5

Nếu bạn muốn một bảng có khoảng cách cột và hàng, thì hãy thử bảng điều khiển thư viện của tôi

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

Đầu ra nào:

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

ERROR: Spans must be a list of lists
cz

2

Tôi biết câu hỏi này hơi cũ nhưng đây là nỗ lực của tôi về điều này:

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

Nó là IMHO dễ đọc hơn một chút (mặc dù nó không phân biệt giữa các hàng đơn / nhiều như các giải pháp của @ MattH, cũng như không sử dụng NamedTuples).


2

Tôi sử dụng chức năng tiện ích nhỏ này.

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'])

đầu ra

-----------------
|header 1|header 2|
-----------------
|1       |2       |
|3       |4       |
-----------------

1
Bạn đang thêm khoảng cách giữa mỗi cột output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '\n' nhưng không thêm vào các dòng phân tách. Có thể mở rộng mà dãy -s với một cái gì đó đơn giản như output = '-' * (sum(max_len) + 1 + len(header)) + '\n'
ochawkeye

1

Đây là giải pháp của tôi:

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)

1
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()

Thí dụ:

table = {
        "uid": ["0", "1", "2", "3"],
        "name": ["Jon", "Doe", "Lemma", "Hemma"]
    }

create_table(table)

Đầu ra:

uid   | name       | 
------+------------+-
0     | Jon        | 
1     | Doe        | 
2     | Lemma      | 
3     | Hemma      | 
------+------------+-

2
Bạn có thể cải thiện câu trả lời chỉ có mã của mình bằng cách bổ sung cho nó một số giải thích.
Yunnosch

0

Điều này có thể được thực hiện chỉ với các mô-đun nội trang khá nhỏ gọn bằng cách sử dụng hiểu danh sách và chuỗi. Chấp nhận danh sách các từ điển có cùng định dạng ...

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.