Python으로 ASCII 테이블을 어떻게 예쁘게 인쇄 할 수 있습니까? [닫은]


81

다음과 같이 테이블을 예쁘게 인쇄하는 방법을 찾고 있습니다.

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

asciitable 라이브러리를 찾았 지만 테두리 등을 수행하지 않습니다. 복잡한 데이터 항목 서식이 필요하지 않으며 문자열 일뿐입니다. 열 크기를 자동으로 조정하는 데 필요합니다.

다른 라이브러리 또는 메소드가 존재합니까? 아니면 직접 작성하는 데 몇 분이 소요됩니까?


docutils를 사용하여이 작업을 수행하는 것은 어떻습니까?
— S.Lott

당신은 테이블을 무엇이라고 부릅니까? 데이터는 테이블에 어떻게 구성되어 있습니까? value1, value2, value3, value4 ... 목록의 연속 값입니까? fomat ()만으로도 그렇게 간단한 디스플레이를 얻을 수 있다고 생각합니다. 오랜 시간 동안 라이브러리를 사용하여 시간을 얻는 방법을 설명하는 자습서를 배울 필요가 없습니다
— eyquem

2
@korona : 아니요, 제안을하지 않았습니다. 나는 질문을하고 있었다. @kdt가 무엇을 알고 있는지 모르는지 전혀 모릅니다. 짐작하기보다는 물어야한다는 강박감을 느낍니다.
— S.Lott

5
실제로 그가 docutils에 대해 알고 있다고 가정하는 것처럼 들렸습니다. 아닐까요?
— korona

2
@ S.Lott 저는 docutils를 살펴 봤고 물론 텍스트를 html, latex 등으로 변환하는 데는 좋지만, 정렬되고 예쁘게 보이는 열이있는 멋진 텍스트 테이블 을 생성 하는 방법을 찾지 못했습니다. 고정 너비 글꼴. kdt의 목표를 오해 했습니까? 아니면 뭔가 놓치고 있습니까?
— nealmcb

답변:


71

나는이 질문을 오래 전에 읽었고 테이블에 대한 내 자신의 예쁜 프린터 작성을 마쳤습니다 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쉽게 이맥스에서 편집 할 수 있습니다.

성능 측면에서는 tabulate보다 약간 느리지 asciitable만 PrettyTable및 보다 훨씬 빠릅니다 texttable.

추신 나는 또한 소수 열로 숫자를 정렬하는 것을 좋아 합니다. 따라서 이것은 숫자에 대한 기본 정렬입니다 (재정의 가능).


4
나는 방금 표 작성 솔루션이 필요했고 운이 좋게도 도서관을 찾을 수있었습니다! 매력처럼 작동합니다 : D에서 케이스 당신에게있는 거 듣기, 그냥 말하고 싶었다 감사합니다 :)
— 디팍

2
예, 듣고 있습니다. 친절한 말씀 감사합니다. 긍정적 인 피드백을받는 것이 정말 좋습니다.
— sastanin

1
안녕하세요, @sastanin 우선 이렇게 멋진 라이브러리를 만들어 주셔서 감사합니다. 터미널의 전체 너비에 걸쳐 표를 인쇄하는 옵션이 있는지 알 수 있습니까?
— Validus Oculus

1
안녕하세요 sastanin,이 매우 편리한 패키지에 대해 감사의 말을 전하고 싶었습니다. 매력처럼 작동하고 직접 쓰는 수고를 덜어주었습니다. 공유 해주셔서 감사합니다!
— Valentin B.

1
귀하의 기능 목록은 과소 평가입니다. ansi 탈출 물건을 시도하고 완벽하게 작동합니다. 감사합니다!
— Red Pill

37

다음은 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

@MattH 예제를 통해이 함수의 사용법을 보여줄 수 있습니까?
— theAlse

1
@MattH 감사하지만 큰 숫자는 즉시 충돌하는 것 같습니다. TypeError : 'int'유형의 객체에 len ()이 없습니다.
— theAlse

@Alborz : 나는 이것을 다른 사람들을위한 출발점으로 게시했고, 원하는 경우 데이터 유형을 처리하도록 사용자 정의했습니다. 의도 한대로 어떤 오류가, 당신은 함수를 호출되지 않을 수 있습니다에서 온 그 라인에 따라 비록
— MattH

1
@theAlse 확인하신 버그를 len(str(max(...)))lens.append 라인에 작성하여 수정 했습니다 . 따라서 이제 열의 숫자가 열 머리글보다 더 넓은 경우에도 괜찮습니다. BTW, MattH-max ()에 대한 "key"인수의 귀여운 사용!
— nealmcb

19

내 구글 검색에서 'docutils'를 포함 몇 가지 이유를 들어 나는 우연히 발견 texttable 내가 무엇을 찾고 될 것으로 보인다.


2
멋지네요. 자동 열 너비 감지가 부족합니다. 사용 : pastebin.com/SAsPJUxM
— Kos

12

나도 이것에 대한 내 자신의 해결책을 썼습니다. 나는 그것을 단순하게 유지하려고 노력했다.

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

이 목적을 위해 방금 용어 표 를 출시 했습니다 . 예를 들어,

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

당신을 얻습니다

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

기본적으로 테이블은 유니 코드 상자 그리기 문자로 렌더링됩니다 .

┌─────────────────┬─────────────────┬─────────────────┐
│ a               │       bb        │             ccc │
╞═════════════════╪═════════════════╪═════════════════╡
│ 1               │        2        │               3 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │
└─────────────────┴─────────────────┴─────────────────┘

termtable은 매우 구성 가능합니다. 더 많은 예를 보려면 테스트 를 확인하십시오 .


표시 할 최대 열을 설정하고 라이브러리가 래핑 논리를 처리하도록 할 수 있기를 바랍니다.
— Kang Min Yoo

7

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

/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

유형을 처리하도록 설계된 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    │
└─────┴───────┴─────┘

5

열 및 행 범위가있는 테이블을 원한다면 내 라이브러리 대시 테이블을 사용해보십시오

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

ERROR: Spans must be a list of lists
— cz

2

나는 질문이 조금 오래되었다는 것을 알고 있지만 여기에 대한 나의 시도가 있습니다.

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

좀 더 읽기 쉬운 IMHO입니다 (@MattH의 솔루션처럼 단일 / 다중 행을 구분하지 않고 NamedTuples를 사용하지도 않음).


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

1
output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '\n' 구분선이 아닌 각 열 사이에 공백을 추가합니다 . -s의 행을 다음과 같이 간단한 것으로 확장 할 수 있습니다.output = '-' * (sum(max_len) + 1 + len(header)) + '\n'
— ochawkeye 2011

1

내 해결책은 다음과 같습니다.

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

예:

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

create_table(table)

산출:

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

2
설명을 추가하여 코드 전용 답변을 개선 할 수 있습니다.
— Yunnosch

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.