【问题标题】:Create nice column output in python在 python 中创建漂亮的列输出
【发布时间】:2012-04-16 21:00:11
【问题描述】:

我正在尝试在 python 中创建一个不错的列列表,以便与我创建的命令行管理工具一起使用。

基本上,我想要一个类似的列表:

[['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]

变成:

a            b            c
aaaaaaaaaa   b            c
a            bbbbbbbbbb   c

在这里使用纯制表符无法解决问题,因为我不知道每行中最长的数据。

这与 Linux 中的“column -t”行为相同..

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c"
a b c
aaaaaaaaaa b c
a bbbbbbbbbb c

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c" | column -t
a           b           c
aaaaaaaaaa  b           c
a           bbbbbbbbbb  c

我已经四处寻找各种 python 库来执行此操作,但找不到任何有用的东西。

【问题讨论】:

  • 使用 ncurses 显示我想要的大约 10 行小信息有点矫枉过正。但我们正在使用 ncurses 来处理其他内容。

标签: python string list


【解决方案1】:

从 Python 2.6+ 开始,您可以通过以下方式使用 format string 将列设置为至少 20 个字符并将文本右对齐。

table_data = [
    ['a', 'b', 'c'],
    ['aaaaaaaaaa', 'b', 'c'], 
    ['a', 'bbbbbbbbbb', 'c']
]
for row in table_data:
    print("{: >20} {: >20} {: >20}".format(*row))

输出:

               a                    b                    c
      aaaaaaaaaa                    b                    c
               a           bbbbbbbbbb                    c

【讨论】:

  • 目前为止最好的解决方案
  • 当我尝试使用它时,它只显示 9 个项目。
  • 添加到来自 KurzedMetal 的解决方案中,格式说明符如上所示; {: >20},> 表示右对齐。通过使用 {:
  • 语法是{:>20.2f},如果你想和其他格式说明符结合,比如`.2f`。
  • 我认为这并不能回答问题——听起来 OP 想让每一行的宽度不超过容纳其内容所需的宽度。这只是设置了 20 的固定宽度。
【解决方案2】:
data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]

col_width = max(len(word) for row in data for word in row) + 2  # padding
for row in data:
    print "".join(word.ljust(col_width) for word in row)

a            b            c            
aaaaaaaaaa   b            c            
a            bbbbbbbbbb   c   

它的作用是计算最长的数据条目以确定列宽,然后在打印出每一列时使用.ljust() 添加必要的填充。

【讨论】:

  • 名称longest 具有误导性,因为它不是最长的元素,而是最大长度。顺便说一句,最长的可以用类似的东西:max((w for sub in data for w in sub), key=len)。 [附:我不是投反对票的人]
  • max((w for ...), key=len) 为您提供最长的条目,然后您需要再次运行len。无法决定哪个是明确的,所以我坚持第一个。关于具有误导性的 var 名称的要点。改变了。
  • 是的,两者没有太大区别,只是我想说的口味问题。除此之外,正如您所注意到的,这条线有点(太)混淆了。最好直接做:max(len(x) for sub in data for x in sub),这样也不会建立不必要的列表。
  • 谢谢!这正是我所需要的。但是,我也必须让它与 python 2.4 一起工作,所以我删除了 chain.from_iterable,并按照建议将 col_width 替换为 max(len(x) for sub in data for x in sub) + 2。希望您可以更改上面的代码,以明确其他人是否希望它与 2.4 一起运行。
  • 这将使所有列的宽度相同,这不是column -t 所做的。
【解决方案3】:

我带着同样的要求来到这里,但@lvc 和@Preet 的答案似乎更符合column -t 在列具有不同宽度时产生的内容:

>>> rows =  [   ['a',           'b',            'c',    'd']
...         ,   ['aaaaaaaaaa',  'b',            'c',    'd']
...         ,   ['a',           'bbbbbbbbbb',   'c',    'd']
...         ]
...

>>> widths = [max(map(len, col)) for col in zip(*rows)]
>>> for row in rows:
...     print "  ".join((val.ljust(width) for val, width in zip(row, widths)))
...
a           b           c  d
aaaaaaaaaa  b           c  d
a           bbbbbbbbbb  c  d

【讨论】:

  • 不错。这是实际上遵循原始“规范”的最清晰的解决方案。
  • 这是对我有用的解决方案。其他解决方案产生列输出,但这个解决方案对填充和准确的列宽提供了最大的控制。
  • 漂亮的解决方案。对于任何不是字符串的列,只需添加一个额外的映射:map(len, map(str, col))
  • 很棒的答案,谢谢!我将print ... 更改为print string.rstrip(...),这样在终端中的格式会更好一些(由于文本换行,大量尾随空格会显示为不必要的空白行)。
  • 消除最后一列(不可见)填充的更简洁方法是:widths[-1]=0
【解决方案4】:

这有点晚了,我写的一个包的无耻插件,但你也可以查看Columnar包。

它接受一个输入列表列表和一个标题列表,并输出一个表格格式的字符串。这个 sn-p 创建一个 docker-esque 表:

from columnar import columnar

headers = ['name', 'id', 'host', 'notes']

data = [
    ['busybox', 'c3c37d5d-38d2-409f-8d02-600fd9d51239', 'linuxnode-1-292735', 'Test server.'],
    ['alpine-python', '6bb77855-0fda-45a9-b553-e19e1a795f1e', 'linuxnode-2-249253', 'The one that runs python.'],
    ['redis', 'afb648ba-ac97-4fb2-8953-9a5b5f39663e', 'linuxnode-3-3416918', 'For queues and stuff.'],
    ['app-server', 'b866cd0f-bf80-40c7-84e3-c40891ec68f9', 'linuxnode-4-295918', 'A popular destination.'],
    ['nginx', '76fea0f0-aa53-4911-b7e4-fae28c2e469b', 'linuxnode-5-292735', 'Traffic Cop'],
]

table = columnar(data, headers, no_borders=True)
print(table)

或者,您可以对颜色和边框进行一些花哨的操作。

要了解有关列大小算法的更多信息并查看 API 的其余部分,您可以查看上面的链接或查看Columnar GitHub Repo

【讨论】:

    【解决方案5】:

    您必须通过 2 次通过:

    1. 获取每列的最大宽度。
    2. 使用我们从第一次使用str.ljust()str.rjust() 获得的最大宽度知识来格式化列

    【讨论】:

    • 又短又甜。
    【解决方案6】:

    哇,只有 17 个答案。 python 的禅宗说:“应该有一种——最好只有一种——明显的方法。”

    所以这是第 18 种方法: tabulate 包支持一系列可以显示为表格的数据类型,这是一个改编自他们的文档的简单示例:

    from tabulate import tabulate
    
    table = [["Sun",696000,1989100000],
             ["Earth",6371,5973.6],
             ["Moon",1737,73.5],
             ["Mars",3390,641.85]]
    
    print(tabulate(table, headers=["Planet","R (km)", "mass (x 10^29 kg)"]))
    

    哪个输出

    Planet      R (km)    mass (x 10^29 kg)
    --------  --------  -------------------
    Sun         696000           1.9891e+09
    Earth         6371        5973.6
    Moon          1737          73.5
    Mars          3390         641.85
    

    【讨论】:

    • 第一次尝试看起来很不错。它处理列表和列表列表,使其非常易于使用。
    【解决方案7】:

    像这样转置列是 zip 的工作:

    >>> a = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
    >>> list(zip(*a))
    [('a', 'aaaaaaaaaa', 'a'), ('b', 'b', 'bbbbbbbbbb'), ('c', 'c', 'c')]
    

    要查找每列所需的长度,可以使用max

    >>> trans_a = zip(*a)
    >>> [max(len(c) for c in b) for b in trans_a]
    [10, 10, 1]
    

    您可以使用它,通过适当的填充,构造要传递给print的字符串:

    >>> col_lenghts = [max(len(c) for c in b) for b in trans_a]
    >>> padding = ' ' # You might want more
    >>> padding.join(s.ljust(l) for s,l in zip(a[0], col_lenghts))
    'a          b          c'
    

    【讨论】:

      【解决方案8】:

      为了得到更高级的桌子,比如

      ---------------------------------------------------
      | First Name | Last Name        | Age | Position  |
      ---------------------------------------------------
      | John       | Smith            | 24  | Software  |
      |            |                  |     | Engineer  |
      ---------------------------------------------------
      | Mary       | Brohowski        | 23  | Sales     |
      |            |                  |     | Manager   |
      ---------------------------------------------------
      | Aristidis  | Papageorgopoulos | 28  | Senior    |
      |            |                  |     | Reseacher |
      ---------------------------------------------------
      

      你可以使用这个Python recipe:

      '''
      From http://code.activestate.com/recipes/267662-table-indentation/
      PSF License
      '''
      import cStringIO,operator
      
      def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left',
                 separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x):
          """Indents a table by column.
             - rows: A sequence of sequences of items, one sequence per row.
             - hasHeader: True if the first row consists of the columns' names.
             - headerChar: Character to be used for the row separator line
               (if hasHeader==True or separateRows==True).
             - delim: The column delimiter.
             - justify: Determines how are data justified in their column. 
               Valid values are 'left','right' and 'center'.
             - separateRows: True if rows are to be separated by a line
               of 'headerChar's.
             - prefix: A string prepended to each printed row.
             - postfix: A string appended to each printed row.
             - wrapfunc: A function f(text) for wrapping text; each element in
               the table is first wrapped by this function."""
          # closure for breaking logical rows to physical, using wrapfunc
          def rowWrapper(row):
              newRows = [wrapfunc(item).split('\n') for item in row]
              return [[substr or '' for substr in item] for item in map(None,*newRows)]
          # break each logical row into one or more physical ones
          logicalRows = [rowWrapper(row) for row in rows]
          # columns of physical rows
          columns = map(None,*reduce(operator.add,logicalRows))
          # get the maximum of each column by the string length of its items
          maxWidths = [max([len(str(item)) for item in column]) for column in columns]
          rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
                                       len(delim)*(len(maxWidths)-1))
          # select the appropriate justify method
          justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()]
          output=cStringIO.StringIO()
          if separateRows: print >> output, rowSeparator
          for physicalRows in logicalRows:
              for row in physicalRows:
                  print >> output, \
                      prefix \
                      + delim.join([justify(str(item),width) for (item,width) in zip(row,maxWidths)]) \
                      + postfix
              if separateRows or hasHeader: print >> output, rowSeparator; hasHeader=False
          return output.getvalue()
      
      # written by Mike Brown
      # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
      def wrap_onspace(text, width):
          """
          A word-wrap function that preserves existing line breaks
          and most spaces in the text. Expects that existing line
          breaks are posix newlines (\n).
          """
          return reduce(lambda line, word, width=width: '%s%s%s' %
                        (line,
                         ' \n'[(len(line[line.rfind('\n')+1:])
                               + len(word.split('\n',1)[0]
                                    ) >= width)],
                         word),
                        text.split(' ')
                       )
      
      import re
      def wrap_onspace_strict(text, width):
          """Similar to wrap_onspace, but enforces the width constraint:
             words longer than width are split."""
          wordRegex = re.compile(r'\S{'+str(width)+r',}')
          return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width)
      
      import math
      def wrap_always(text, width):
          """A simple word-wrap function that wraps text on exactly width characters.
             It doesn't split the text in words."""
          return '\n'.join([ text[width*i:width*(i+1)] \
                             for i in xrange(int(math.ceil(1.*len(text)/width))) ])
      
      if __name__ == '__main__':
          labels = ('First Name', 'Last Name', 'Age', 'Position')
          data = \
          '''John,Smith,24,Software Engineer
             Mary,Brohowski,23,Sales Manager
             Aristidis,Papageorgopoulos,28,Senior Reseacher'''
          rows = [row.strip().split(',')  for row in data.splitlines()]
      
          print 'Without wrapping function\n'
          print indent([labels]+rows, hasHeader=True)
          # test indent with different wrapping functions
          width = 10
          for wrapper in (wrap_always,wrap_onspace,wrap_onspace_strict):
              print 'Wrapping function: %s(x,width=%d)\n' % (wrapper.__name__,width)
              print indent([labels]+rows, hasHeader=True, separateRows=True,
                           prefix='| ', postfix=' |',
                           wrapfunc=lambda x: wrapper(x,width))
      
          # output:
          #
          #Without wrapping function
          #
          #First Name | Last Name        | Age | Position         
          #-------------------------------------------------------
          #John       | Smith            | 24  | Software Engineer
          #Mary       | Brohowski        | 23  | Sales Manager    
          #Aristidis  | Papageorgopoulos | 28  | Senior Reseacher 
          #
          #Wrapping function: wrap_always(x,width=10)
          #
          #----------------------------------------------
          #| First Name | Last Name  | Age | Position   |
          #----------------------------------------------
          #| John       | Smith      | 24  | Software E |
          #|            |            |     | ngineer    |
          #----------------------------------------------
          #| Mary       | Brohowski  | 23  | Sales Mana |
          #|            |            |     | ger        |
          #----------------------------------------------
          #| Aristidis  | Papageorgo | 28  | Senior Res |
          #|            | poulos     |     | eacher     |
          #----------------------------------------------
          #
          #Wrapping function: wrap_onspace(x,width=10)
          #
          #---------------------------------------------------
          #| First Name | Last Name        | Age | Position  |
          #---------------------------------------------------
          #| John       | Smith            | 24  | Software  |
          #|            |                  |     | Engineer  |
          #---------------------------------------------------
          #| Mary       | Brohowski        | 23  | Sales     |
          #|            |                  |     | Manager   |
          #---------------------------------------------------
          #| Aristidis  | Papageorgopoulos | 28  | Senior    |
          #|            |                  |     | Reseacher |
          #---------------------------------------------------
          #
          #Wrapping function: wrap_onspace_strict(x,width=10)
          #
          #---------------------------------------------
          #| First Name | Last Name  | Age | Position  |
          #---------------------------------------------
          #| John       | Smith      | 24  | Software  |
          #|            |            |     | Engineer  |
          #---------------------------------------------
          #| Mary       | Brohowski  | 23  | Sales     |
          #|            |            |     | Manager   |
          #---------------------------------------------
          #| Aristidis  | Papageorgo | 28  | Senior    |
          #|            | poulos     |     | Reseacher |
          #---------------------------------------------
      

      Python recipe page 包含一些改进。

      【讨论】:

        【解决方案9】:

        pandas 基于创建数据框的解决方案:

        import pandas as pd
        l = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
        df = pd.DataFrame(l)
        
        print(df)
                    0           1  2
        0           a           b  c
        1  aaaaaaaaaa           b  c
        2           a  bbbbbbbbbb  c
        

        要删除索引和标题值以创建您想要的输出,您可以使用to_string 方法:

        result = df.to_string(index=False, header=False)
        
        print(result)
                  a           b  c
         aaaaaaaaaa           b  c
                  a  bbbbbbbbbb  c
        

        【讨论】:

          【解决方案10】:

          Scolp 是一个新库,可让您轻松漂亮地打印流式列数据,同时自动调整列宽。

          (免责声明:我是作者)

          【讨论】:

            【解决方案11】:

            对于懒惰的人

            使用 Python 3.*Pandas/Geopandas;通用简单的类内方法(对于“普通”脚本只需删除 self):

            函数着色:

                def colorize(self,s,color):
                    s = color+str(s)+"\033[0m"
                    return s
            

            标题:

            print('{0:<23} {1:>24} {2:>26} {3:>26} {4:>11} {5:>11}'.format('Road name','Classification','Function','Form of road','Length','Distance') )
            

            然后是来自 Pandas/Geopandas 数据框的数据:

                        for index, row in clipped.iterrows():
                            rdName      = self.colorize(row['name1'],"\033[32m")
                            rdClass     = self.colorize(row['roadClassification'],"\033[93m")
                            rdFunction  = self.colorize(row['roadFunction'],"\033[33m")
                            rdForm      = self.colorize(row['formOfWay'],"\033[94m")
                            rdLength    = self.colorize(row['length'],"\033[97m")
                            rdDistance  = self.colorize(row['distance'],"\033[96m")
                            print('{0:<30} {1:>35} {2:>35} {3:>35} {4:>20} {5:>20}'.format(rdName,rdClass,rdFunction,rdForm,rdLength,rdDistance) )
            

            {0:&lt;30} {1:&gt;35} {2:&gt;35} {3:&gt;35} {4:&gt;20} {5:&gt;20}的含义:

            0, 1, 2, 3, 4, 5 -> 列,本例一共6列

            30, 35, 20 -> 列宽(请注意,您必须添加 \033[96m 的长度 - 这对于 Python 也是一个字符串),只是实验 :)

            &gt;, &lt; -> justify: right, left(= 也用于填充零)

            如果你想区分例如最大值,您必须切换到特殊的 Pandas 样式功能,但假设这足以在终端窗口上显示数据。

            结果:

            【讨论】:

              【解决方案12】:

              与之前的答案略有不同(我没有足够的代表对此发表评论)。格式库允许您指定元素的宽度和对齐方式,但不能指定元素的开始位置,即您可以说“宽 20 列”但不能说“从第 20 列开始”。这导致了这个问题:

              table_data = [
                  ['a', 'b', 'c'],
                  ['aaaaaaaaaa', 'b', 'c'], 
                  ['a', 'bbbbbbbbbb', 'c']
              ]
              
              print("first row: {: >20} {: >20} {: >20}".format(*table_data[0]))
              print("second row: {: >20} {: >20} {: >20}".format(*table_data[1]))
              print("third row: {: >20} {: >20} {: >20}".format(*table_data[2]))
              

              输出

              first row:                    a                    b                    c
              second row:           aaaaaaaaaa                    b                    c
              third row:                    a           bbbbbbbbbb                    c
              

              答案当然是格式化文字字符串,它与格式的结合有点奇怪:

              table_data = [
                  ['a', 'b', 'c'],
                  ['aaaaaaaaaa', 'b', 'c'], 
                  ['a', 'bbbbbbbbbb', 'c']
              ]
              
              print(f"{'first row:': <20} {table_data[0][0]: >20} {table_data[0][1]: >20} {table_data[0][2]: >20}")
              print("{: <20} {: >20} {: >20} {: >20}".format(*['second row:', *table_data[1]]))
              print("{: <20} {: >20} {: >20} {: >20}".format(*['third row:', *table_data[1]]))
              

              输出

              first row:                              a                    b                    c
              second row:                    aaaaaaaaaa                    b                    c
              third row:                     aaaaaaaaaa                    b                    c
              

              【讨论】:

                【解决方案13】:

                这会根据其他答案中使用的最大度量来设置独立的、最适合的列宽。

                data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
                padding = 2
                col_widths = [max(len(w) for w in [r[cn] for r in data]) + padding for cn in range(len(data[0]))]
                format_string = "{{:{}}}{{:{}}}{{:{}}}".format(*col_widths)
                for row in data:
                    print(format_string.format(*row))
                

                【讨论】:

                • 这是最好的答案,虽然代码有点臭。
                【解决方案14】:

                我发现这个答案非常有用和优雅,最初来自here

                matrix = [["A", "B"], ["C", "D"]]
                
                print('\n'.join(['\t'.join([str(cell) for cell in row]) for row in matrix]))
                

                输出

                A   B
                C   D
                

                【讨论】:

                  【解决方案15】:

                  这是 Shawn Chin 答案的变体。宽度是每列固定的,而不是所有列。第一行下方和列之间还有一个边框。 (icontract 库用于执行合同。)

                  @icontract.pre(
                      lambda table: not table or all(len(row) == len(table[0]) for row in table))
                  @icontract.post(lambda table, result: result == "" if not table else True)
                  @icontract.post(lambda result: not result.endswith("\n"))
                  def format_table(table: List[List[str]]) -> str:
                      """
                      Format the table as equal-spaced columns.
                  
                      :param table: rows of cells
                      :return: table as string
                      """
                      cols = len(table[0])
                  
                      col_widths = [max(len(row[i]) for row in table) for i in range(cols)]
                  
                      lines = []  # type: List[str]
                      for i, row in enumerate(table):
                          parts = []  # type: List[str]
                  
                          for cell, width in zip(row, col_widths):
                              parts.append(cell.ljust(width))
                  
                          line = " | ".join(parts)
                          lines.append(line)
                  
                          if i == 0:
                              border = []  # type: List[str]
                  
                              for width in col_widths:
                                  border.append("-" * width)
                  
                              lines.append("-+-".join(border))
                  
                      result = "\n".join(lines)
                  
                      return result
                  

                  这是一个例子:

                  >>> table = [['column 0', 'another column 1'], ['00', '01'], ['10', '11']]
                  >>> result = packagery._format_table(table=table)
                  >>> print(result)
                  column 0 | another column 1
                  ---------+-----------------
                  00       | 01              
                  10       | 11              
                  

                  【讨论】:

                    【解决方案16】:

                    更新了@Franck Dernoncourt 花式配方,使其符合 python 3 和 PEP8

                    import io
                    import math
                    import operator
                    import re
                    import functools
                    
                    from itertools import zip_longest
                    
                    
                    def indent(
                        rows,
                        has_header=False,
                        header_char="-",
                        delim=" | ",
                        justify="left",
                        separate_rows=False,
                        prefix="",
                        postfix="",
                        wrapfunc=lambda x: x,
                    ):
                        """Indents a table by column.
                           - rows: A sequence of sequences of items, one sequence per row.
                           - hasHeader: True if the first row consists of the columns' names.
                           - headerChar: Character to be used for the row separator line
                             (if hasHeader==True or separateRows==True).
                           - delim: The column delimiter.
                           - justify: Determines how are data justified in their column.
                             Valid values are 'left','right' and 'center'.
                           - separateRows: True if rows are to be separated by a line
                             of 'headerChar's.
                           - prefix: A string prepended to each printed row.
                           - postfix: A string appended to each printed row.
                           - wrapfunc: A function f(text) for wrapping text; each element in
                             the table is first wrapped by this function."""
                    
                        # closure for breaking logical rows to physical, using wrapfunc
                        def row_wrapper(row):
                            new_rows = [wrapfunc(item).split("\n") for item in row]
                            return [[substr or "" for substr in item] for item in zip_longest(*new_rows)]
                    
                        # break each logical row into one or more physical ones
                        logical_rows = [row_wrapper(row) for row in rows]
                        # columns of physical rows
                        columns = zip_longest(*functools.reduce(operator.add, logical_rows))
                        # get the maximum of each column by the string length of its items
                        max_widths = [max([len(str(item)) for item in column]) for column in columns]
                        row_separator = header_char * (
                            len(prefix) + len(postfix) + sum(max_widths) + len(delim) * (len(max_widths) - 1)
                        )
                        # select the appropriate justify method
                        justify = {"center": str.center, "right": str.rjust, "left": str.ljust}[
                            justify.lower()
                        ]
                        output = io.StringIO()
                        if separate_rows:
                            print(output, row_separator)
                        for physicalRows in logical_rows:
                            for row in physicalRows:
                                print( output, prefix + delim.join(
                                    [justify(str(item), width) for (item, width) in zip(row, max_widths)]
                                ) + postfix)
                            if separate_rows or has_header:
                                print(output, row_separator)
                                has_header = False
                        return output.getvalue()
                    
                    
                    # written by Mike Brown
                    # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
                    def wrap_onspace(text, width):
                        """
                        A word-wrap function that preserves existing line breaks
                        and most spaces in the text. Expects that existing line
                        breaks are posix newlines (\n).
                        """
                        return functools.reduce(
                            lambda line, word, i_width=width: "%s%s%s"
                            % (
                                line,
                                " \n"[
                                    (
                                        len(line[line.rfind("\n") + 1 :]) + len(word.split("\n", 1)[0])
                                        >= i_width
                                    )
                                ],
                                word,
                            ),
                            text.split(" "),
                        )
                    
                    
                    def wrap_onspace_strict(text, i_width):
                        """Similar to wrap_onspace, but enforces the width constraint:
                           words longer than width are split."""
                        word_regex = re.compile(r"\S{" + str(i_width) + r",}")
                        return wrap_onspace(
                            word_regex.sub(lambda m: wrap_always(m.group(), i_width), text), i_width
                        )
                    
                    
                    def wrap_always(text, width):
                        """A simple word-wrap function that wraps text on exactly width characters.
                           It doesn't split the text in words."""
                        return "\n".join(
                            [
                                text[width * i : width * (i + 1)]
                                for i in range(int(math.ceil(1.0 * len(text) / width)))
                            ]
                        )
                    
                    
                    if __name__ == "__main__":
                        labels = ("First Name", "Last Name", "Age", "Position")
                        data = """John,Smith,24,Software Engineer
                               Mary,Brohowski,23,Sales Manager
                               Aristidis,Papageorgopoulos,28,Senior Reseacher"""
                        rows = [row.strip().split(",") for row in data.splitlines()]
                    
                        print("Without wrapping function\n")
                        print(indent([labels] + rows, has_header=True))
                    
                        # test indent with different wrapping functions
                        width = 10
                        for wrapper in (wrap_always, wrap_onspace, wrap_onspace_strict):
                            print("Wrapping function: %s(x,width=%d)\n" % (wrapper.__name__, width))
                    
                            print(
                                indent(
                                    [labels] + rows,
                                    has_header=True,
                                    separate_rows=True,
                                    prefix="| ",
                                    postfix=" |",
                                    wrapfunc=lambda x: wrapper(x, width),
                                )
                            )
                    
                        # output:
                        #
                        # Without wrapping function
                        #
                        # First Name | Last Name        | Age | Position
                        # -------------------------------------------------------
                        # John       | Smith            | 24  | Software Engineer
                        # Mary       | Brohowski        | 23  | Sales Manager
                        # Aristidis  | Papageorgopoulos | 28  | Senior Reseacher
                        #
                        # Wrapping function: wrap_always(x,width=10)
                        #
                        # ----------------------------------------------
                        # | First Name | Last Name  | Age | Position   |
                        # ----------------------------------------------
                        # | John       | Smith      | 24  | Software E |
                        # |            |            |     | ngineer    |
                        # ----------------------------------------------
                        # | Mary       | Brohowski  | 23  | Sales Mana |
                        # |            |            |     | ger        |
                        # ----------------------------------------------
                        # | Aristidis  | Papageorgo | 28  | Senior Res |
                        # |            | poulos     |     | eacher     |
                        # ----------------------------------------------
                        #
                        # Wrapping function: wrap_onspace(x,width=10)
                        #
                        # ---------------------------------------------------
                        # | First Name | Last Name        | Age | Position  |
                        # ---------------------------------------------------
                        # | John       | Smith            | 24  | Software  |
                        # |            |                  |     | Engineer  |
                        # ---------------------------------------------------
                        # | Mary       | Brohowski        | 23  | Sales     |
                        # |            |                  |     | Manager   |
                        # ---------------------------------------------------
                        # | Aristidis  | Papageorgopoulos | 28  | Senior    |
                        # |            |                  |     | Reseacher |
                        # ---------------------------------------------------
                        #
                        # Wrapping function: wrap_onspace_strict(x,width=10)
                        #
                        # ---------------------------------------------
                        # | First Name | Last Name  | Age | Position  |
                        # ---------------------------------------------
                        # | John       | Smith      | 24  | Software  |
                        # |            |            |     | Engineer  |
                        # ---------------------------------------------
                        # | Mary       | Brohowski  | 23  | Sales     |
                        # |            |            |     | Manager   |
                        # ---------------------------------------------
                        # | Aristidis  | Papageorgo | 28  | Senior    |
                        # |            | poulos     |     | Reseacher |
                        # ---------------------------------------------
                    

                    【讨论】:

                      【解决方案17】:

                      您可以准备数据并将其传递给真正的column 实用程序。

                      假设您已将数据打印到文件 /tmp/filename.txt 中,并使用选项卡作为分隔符。然后你可以像这样对它进行分列:

                      import subprocess
                      
                      result = subprocess.run("cat /tmp/filename.txt | column -N \"col_1,col_2,col_3\" -t -s'\t' -R 2,3", shell=True, stdout=subprocess.PIPE)
                      print(result.stdout.decode("utf-8"))
                      

                      如您所见,您可以使用列实用程序的功能,例如右对齐。

                      【讨论】:

                        【解决方案18】:
                        table = [['a', 'b', 'c'],
                                 ['aaaaaaaaaa', 'b', 'c'],
                                 ['a', 'bbbbbbbbbb', 'c']]
                        
                        
                        def print_table(table):
                            def get_fmt(table):
                                fmt = ''
                                for column, row in enumerate(table[0]):
                                    fmt += '{{!s:<{}}} '.format(
                                        max(len(str(row[column])) for row in table) + 2)
                                return fmt
                            fmt = get_fmt(table)
                            for row in table:
                                print(fmt.format(*row))
                        
                        
                        print_table(table)
                        

                        【讨论】:

                          【解决方案19】:

                          格式化为表格需要正确的填充。一种通用的解决方案是使用名为prettytable 的python 包。虽然不依赖库会更好,但是这个包处理了所有的边缘情况,并且很简单,没有任何进一步的依赖。

                          x = PrettyTable()
                          x.field_names =["field1", "field2", "field3"]
                          x.add_row(["col1_content", "col2_content", "col3_content"])
                          print(x)
                          

                          【讨论】:

                            【解决方案20】:

                            我意识到这个问题很老,但我不理解 Antak 的答案,也不想使用库,所以我推出了自己的解决方案。

                            解决方案假设记录是一个二维数组,记录都是相同的长度,并且字段都是字符串。

                            def stringifyRecords(records):
                                column_widths = [0] * len(records[0])
                                for record in records:
                                    for i, field in enumerate(record):
                                        width = len(field)
                                        if width > column_widths[i]: column_widths[i] = width
                            
                                s = ""
                                for record in records:
                                    for column_width, field in zip(column_widths, record):
                                        s += field.ljust(column_width+1)
                                    s += "\n"
                            
                                return s
                            

                            【讨论】:

                              猜你喜欢
                              • 2013-02-09
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2014-12-13
                              • 2014-08-16
                              • 2018-12-10
                              相关资源
                              最近更新 更多