像列输出一样 - python 库

问题描述 投票:0回答:4

Debian 的 apt 工具输出结果是统一宽度的列。例如,尝试运行“aptitude search svn”..所有名称都会出现在相同宽度的第一列中。

现在,如果您调整终端大小,列宽也会相应调整。

是否有一个 Python 库可以实现这一点?请注意,库必须了解终端宽度并采用表格作为输入 - 例如,可以是

[('rapidsvn', 'A GUI client for subversion'), ...]
..,您还可以指定第一列(或任何列)的最大宽度。另请注意,如果超出终端宽度,则如何修剪下面第二列中的字符串..从而不会引入不需要的第二行。

$ aptitude search svn
[...]
p   python-svn-dbg                    - A(nother) Python interface to Subversion (d
v   python2.5-svn                     -                                            
v   python2.6-svn                     -                                            
p   rapidsvn                          - A GUI client for subversion                
p   statsvn                           - SVN repository statistics                  
p   svn-arch-mirror                   - one-way mirroring from Subversion to Arch r
p   svn-autoreleasedeb                - Automatically release/upload debian package
p   svn-buildpackage                  - helper programs to maintain Debian packages
p   svn-load                          - An enhanced import facility for Subversion 
p   svn-workbench                     - A Workbench for Subversion                 
p   svnmailer                         - extensible Subversion commit notification t
p   websvn                            - interface for subversion repositories writt
$

编辑:(回应下面亚历克斯的答案)...输出将类似于“能力搜索”,其中1)只有最后一列(这是一行中唯一具有最长字符串的列)是被修剪,2) 通常只有 2-4 列,但最后一列(“描述”)预计至少占据终端宽度的一半。 3) 所有行包含相同数量的列,4) 所有条目仅是字符串

python terminal formatting apt
4个回答
4
投票

更新

colprint
例程现已在applibPython库托管于GitHub中可用。

以下是感兴趣的人的完整计划:

# This function was written by Alex Martelli
# http://stackoverflow.com/questions/1396820/
def colprint(table, totwidth=None):
    """Print the table in terminal taking care of wrapping/alignment

    - `table`:    A table of strings. Elements must not be `None`
    - `totwidth`: If None, console width is used
    """
    if not table: return
    if totwidth is None:
        totwidth = find_console_width()
        totwidth -= 1 # for not printing an extra empty line on windows
    numcols = max(len(row) for row in table)
    # ensure all rows have >= numcols columns, maybe empty
    padded = [row+numcols*('',) for row in table]
    # compute col widths, including separating space (except for last one)
    widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
    widths[-1] -= 1
    # drop or truncate columns from the right in order to fit
    while sum(widths) > totwidth:
        mustlose = sum(widths) - totwidth
        if widths[-1] <= mustlose:
            del widths[-1]
        else:
            widths[-1] -= mustlose
            break
    # and finally, the output phase!
    for row in padded:
        print(''.join([u'%*s' % (-w, i[:w])
                       for w, i in zip(widths, row)]))

def find_console_width():
    if sys.platform.startswith('win'):
        return _find_windows_console_width()
    else:
        return _find_unix_console_width()
def _find_unix_console_width():
    """Return the width of the Unix terminal

    If `stdout` is not a real terminal, return the default value (80)
    """
    import termios, fcntl, struct, sys

    # fcntl.ioctl will fail if stdout is not a tty
    if not sys.stdout.isatty():
        return 80

    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    height, width = struct.unpack("HHHH", size)[:2]
    return width
def _find_windows_console_width():
    """Return the width of the Windows console

    If the width cannot be determined, return the default value (80)
    """
    # http://code.activestate.com/recipes/440694/
    from ctypes import windll, create_string_buffer
    STDIN, STDOUT, STDERR = -10, -11, -12

    h = windll.kernel32.GetStdHandle(STDERR)
    csbi = create_string_buffer(22)
    res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)

    if res:
        import struct
        (bufx, bufy, curx, cury, wattr,
         left, top, right, bottom,
         maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
        sizex = right - left + 1
        sizey = bottom - top + 1
    else:
        sizex, sizey = 80, 25

    return sizex

2
投票

我认为没有一种通用的跨平台方法来“获取终端的宽度”——绝对不是“查看 COLUMNS 环境变量”(请参阅我对问题的评论)。 在 Linux 和 Mac OS X 上(我希望所有现代 Unix 版本),

curses.wrapper(lambda _: curses.tigetnum('cols'))

返回列数;但我不知道 wcurses 在 Windows 中是否支持此功能。

一旦你确实拥有了(如果你坚持的话,可以从 os.environ['COLUMNS'] ,或者通过诅咒,或者来自预言机,或者默认为 80,或者你喜欢的任何其他方式)所需的输出宽度,剩下的就是 相当可行。这是一项繁琐的工作,有很多机会出现相差一类的错误,并且很容易受到许多您没有完全弄清楚的详细规范的影响,例如:哪一列被剪切以避免换行 - 它总是最后一张,还是……?为什么你在示例输出中显示 3 列,而根据你的问题,只传递了两列......?如果并非所有行都具有相同的列数,会发生什么情况?表中的所有条目都必须是字符串吗?还有很多很多类似的谜团。

因此,对您未表达的所有规格进行有点武断的猜测,一种方法可能类似于......:

import sys

def colprint(totwidth, table):
  numcols = max(len(row) for row in table)
  # ensure all rows have >= numcols columns, maybe empty
  padded = [row+numcols*('',) for row in table]
  # compute col widths, including separating space (except for last one)
  widths = [ 1 + max(len(x) for x in column) for column in zip(*padded)]
  widths[-1] -= 1
  # drop or truncate columns from the right in order to fit
  while sum(widths) > totwidth:
    mustlose = sum(widths) - totwidth
    if widths[-1] <= mustlose:
      del widths[-1]
    else:
      widths[-1] -= mustlose
      break
  # and finally, the output phase!
  for row in padded:
    for w, i in zip(widths, row):
      sys.stdout.write('%*s' % (-w, i[:w]))
    sys.stdout.write('\n')

2
投票

嗯,aptitude 使用

cwidget
来格式化纯文本显示中的列。您可以调用
cwidget
为它编写一个 python 扩展,但我认为这不值得麻烦...您可以使用您首选的方法来获取字符中的实际水平尺寸并自行计算。


2
投票

首先,使用

ioctl
获取 TTY 的大小:

import termios, fcntl, struct, sys

def get_tty_size():
    s = struct.pack("HHHH", 0, 0, 0, 0)
    fd_stdout = sys.stdout.fileno()
    size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
    return struct.unpack("HHHH", size)[:2]

print get_tty_size()

然后使用

ljust
rjust
center
或字符串格式语言来对齐文本:

>>> "foo".ljust(80)
'foo                                                                             '
>>> "foo".rjust(80)
'                                                                             foo'
>>> "foo".center(80)
'                                      foo                                       '
>>> f"{'foo':<80}"
'foo                                                                             '
>>> f"{'foo':>80}"
'                                                                             foo'
>>> f"{'foo':^80}"
'                                      foo                                       '

将它们与一些数学运算放在一起,您就可以调整控制台的列大小!

© www.soinside.com 2019 - 2024. All rights reserved.