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보다 약간 느리지 asciitablePrettyTable및 보다 훨씬 빠릅니다 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 │
╞═════════════════╪═════════════════╪═════════════════╡
│ 123 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236613.23236243236613.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│
├─────┼───────┼─────┤
│123    │
├─────┼───────┼─────┤
│456    │
└─────┴───────┴─────┘

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.