用Python从头开始构建一个包含数据的表格

7
我需要建立一张包含以下数据的表格:
     ┌────────┬───────────┬────────┐
     │ ID     │ Name      │ Age    │
     ├────────┼───────────┼────────┤
     │ 1      │ Jonh      │ 35     │
     ├────────┼───────────┼────────┤
     │ 2      │ Joseph    │ 40     │
     └────────┴───────────┴────────┘

我不被允许使用任何Python库,必须从零开始完成。 我发现有一些框字符可用于绘制表格(https://en.wikipedia.org/wiki/Box-drawing_character)。例如:

print(u'\u250C') -> will print ─

我不确定如何解决这个问题。是先输出数据再画表格呢,还是一行一行完整地输出已经包含了表格的内容。

非常感谢任何帮助。

我目前的工作进展:

length_list = [len(element) for row in data for element in row]
column_width = max(length_list)
for row in data:
   print(u'\u250C' + (u'\u2500'*column_width*len(data[0])) + u'\u2510')
   row = "".join(element.ljust(column_width + 2) for element in row)
   print(row)
   print(u'\u2514' + (u'\u2500'*column_width*len(data[0])) + u'\u2518')

给我这个:
┌──────────────────┐
ID      Name    Age     
└──────────────────┘
┌──────────────────┐
1       John    35      
└──────────────────┘
┌──────────────────┐
2       Joseph  40      
└──────────────────┘
5个回答

5

你已经接近成功了。你可以将任务分成较小的部分,并使用 join() 让你的生活更轻松。

首先,让我们定义一些常量字符来提高代码可读性。

char_line = u'\u2500'
char_lcorner_top = u'\u250C'
char_rcorner_top = u'\u2510'
char_lside = u'\u251C'
char_rside = u'\u2524'
char_top = u'\u252C'
char_bot = u'\u2534'
char_cross = u'\u253C'
char_lcorner_bot = u'\u2514'
char_rcorner_bot = u'\u2518'

现在让我们编写函数来创建行之间的线条:
def top_rule(width, ncols):
    return char_lcorner_top + char_top.join([char_line * width for i in range(ncols)]) + char_rcorner_top

说明:

  • char_line * width- 字符乘以 width 次。比如说 width = 4 ,这将会得到四个横线,像这样:----
  • [char_line * width for i in range(ncols)] 创建一个列表,其中包含 ncols 个元素,每个元素都是 ----
  • char_top.join(...) 使用 字符连接列表的元素。
  • 在返回前,我们在创建的字符串前面添加 ,后面添加

因此,top_rule(4, 3) 会返回 "┌────┬────┬────┐"

同样地,我们可以定义更多的函数:

def bot_rule(width, ncols):
    return char_lcorner_bot + char_bot.join([char_line * width for i in range(ncols)]) + char_rcorner_bot

def mid_rule(width, ncols):
    return char_lside + char_cross.join([char_line * width for i in range(ncols)]) + char_rside

在每一行中,我们有多个需要进行格式化的单元格。让我们编写一个函数来分别为每一行格式化。
def fmt_row(row, width, loffset):
    return "|" + "|".join([cell.ljust(width - loffset).rjust(width) for cell in row]) + "|"

对于行中的每个单元格,我们将单元格文本向左对齐到长度为(width - loffset),并向右对齐到width。然后,使用竖杠(|)进行连接,并在前面和后面添加竖杠。
现在我们只需要调用我们编写的函数即可。请记住,在除了最后一行之外的所有行后,我们只需要打印一个mid_rule。在最后一行之后,我们需要打印一个bot_rule
num_cols = len(data[0])
length_list = [len(element) for row in data for element in row]
column_width = max(length_list) + 2

print(top_rule(column_width, num_cols))
for row in data[:-1]:
    print(fmt_row(row, column_width, 1))
    print(mid_rule(column_width, num_cols))

print(fmt_row(data[-1], column_width, 1))
print(bot_rule(column_width, num_cols))

根据您的数据,您应该得到以下结果:

┌────────┬────────┬────────┐
| ID     | Name   | Age    |
├────────┼────────┼────────┤
| 1      | John   | 35     |
├────────┼────────┼────────┤
| 2      | Joseph | 40     |
└────────┴────────┴────────┘

1

看起来你已经基本掌握了。你需要做的唯一两个小改变是:

  1. 按照间距正确地格式化行(请参见此处),并在条目之间放置管道符。
  2. 每次循环仅打印分隔行一次,而不是两次。您只需要在第一次和最后一次迭代中使用角落字符(将其中一个打印移到循环之外的上方,跟踪您打印的行数(可能使用enumerate),并且仅在最后一次迭代时打印角落字符而不是管道符)

1
我应该先打印数据,然后在周围绘制表格,还是逐行打印完整的带边框的行。在终端上进行打印是逐行进行的。使用Python中基本的print()函数时,没有办法返回到前一行。这意味着您必须在进行打印时同时打印数据和边框。

1

不要打印出方框,你只需要在行之间打印一行表格字符。确保检查是否正在迭代第一行,如果不是,则使用 字符。

然后你需要在每行的开头和结尾添加一个 |

由于表格行的宽度始终相同,所以可以在 for 循环之前计算它,这样打印语句会显得更加清晰。

只要每行中的项目数量相同,以下内容就可以正常工作。

data = [list('abcde'), ['ab', 'cd', 'ef', 'gh', 'aa'], list('abcde')]

length_list = [len(element) for row in data for element in row]
min_gutter_space = 2 # minimum space between columns
column_width = max(length_list) + min_gutter_space
row_width = (len(data[0])+1) * column_width - 1
for i, row in enumerate(data):
    row = ['|', *row, '|']
    if i == 0:
        print('┌' + '─'*row_width + '┐')
    else:
        print('├' + '─'*row_width + '┤')
    print( "".join(element.ljust(column_width) for element in row) )
print('└' + '─'*row_width + '┘')

非常感谢!但我还需要在行元素之间添加竖线。我应该在哪里添加? - anechkayf
我忘记了那个部分,但看起来Pranav的答案完美处理了它。 - hostingutilities.com

1
这可能只是一个小挑战,但你应该尝试清理代码,使其更易于他人理解,同时帮助你理解正在发生的事情,以及重要的是什么出了问题。
例如,我们可以编写一些函数来创建table_row、table_top和table_bottom。然后,我们只需打印table_top,打印table_row(循环每一行),然后打印table_bottom。
为了保持代码整洁(就像你已经做的那样),我们还需要考虑每个列的大小。
def table_row(column_sizes: list, row: list):
    output = "│"
    for idx, value in enumerate(row): # assuming length of row is same as column_sizes
        val = " " + str(value)
        while len(val) < column_sizes[idx]:
            val += " "
        output += val + "│"
    return output

def table_top(column_sizes: list):
    output = "┌" 
    for idx, column_size in enumerate(column_sizes):
        output += "─" * column_size
        if idx != len(column_sizes) - 1:
            output += "┬"
    output += "┐"
    return output

def table_bottom(column_sizes: list):

    output = "└" 
    for idx, column_size in enumerate(column_sizes):
        output += "─" * column_size
        if idx != len(column_sizes) - 1:
            output += "┴"
    output += "┘"
    return output


# Find the size for each column. This could be implemented
# different ways depending on your data format.

data = [
    ["ID", "Name", "Age"],
    [1, "John", 35],
    [2, "Joseph", 40]
] 
column_sizes = [0 for i in range(len(data[0]))] # eg [0, 0, 0]
print(column_sizes)
for row in data:
    for idx,col in enumerate(row):
        if len(str(col)) > column_sizes[idx]: 
            column_sizes[idx] = len(str(col)) + 4 # spaces on either side
print(column_sizes) # [6, 8, 7]
      
print(table_top(column_sizes)) # ┌──────┬────────┬───────┐
for row in data:
    print(table_row(column_sizes, row))
    # │ ID   │ Name   │ Age   │
    # │ 1    │ John   │ 35    │
    # │ 2    │ Joseph │ 40    │

print(table_bottom(column_sizes)) # └──────┴────────┴───────┘


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接