生成 Markdown 表格?

7
有没有办法从对象(Python / Ruby / Java / C#)生成表格?
我想通过编程创建一个简单的表格程序。我有一些对象,我想将某些属性映射到标题,并将集合映射到行。
为什么使用Markdown?因为我以后想手动编辑该文档。现在,整个过程如下:
- 报告引擎是C# - 有一些对象用于生成DOCX(有中间XML或类似的东西) - 我几乎总是需要进行微小的修复,并且必须在MS Word中打开那些docx文件 - 请求开发团队修复每个错误很麻烦,因为他们根本没有时间立即解决问题,我必须等待下一个版本发布。
我已经想出了一个方法,如果我得到Markdown文档,我可以轻松地编辑它,插入一些变量并使用pandoc将这些变量替换为给定数据。但是要获取Markdown,我必须知道开发人员如何在Markdown中生成表格。

1
你没有提到Haskell。如果你能用Haskell编写一个简单的脚本,你可以使用pandoc API(特别是Text.Pandoc.Builder)轻松创建表格,并将它们写入pandoc支持的任何格式,包括markdown。 - John MacFarlane
我没有提到这个,因为我不会Haskell。此刻我在寻找现成的解决方案。<headers, rows> => markdown表格。如果没有这样的解决方案,我将尝试编写类似的脚本,但是我该如何在.NET代码中集成Haskell呢?需要进行一些研究... - Simon
不幸的是,Markdown目前(还)不支持表格,但一些扩展可以。特别是,Doxygen为Markdown提供了一个很好的表格扩展。 - Codie CodeMonkey
R pander包就是你要找的。 - Stéphane Laurent
3个回答

13

我需要做类似的事情来生成Doxygen Markdown表格,所以我想分享一下。我已经在Python 2.7和3.3中成功运行了示例代码,尽管我不能声称我已经进行了严格的测试。

# Generates tables for Doxygen flavored Markdown.  See the Doxygen
# documentation for details:
#   http://www.doxygen.nl/manual/markdown.html#md_tables

# Translation dictionaries for table alignment
left_rule = {'<': ':', '^': ':', '>': '-'}
right_rule = {'<': '-', '^': ':', '>': ':'}

def evalute_field(record, field_spec):
    """
    Evalute a field of a record using the type of the field_spec as a guide.
    """
    if type(field_spec) is int:
        return str(record[field_spec])
    elif type(field_spec) is str:
        return str(getattr(record, field_spec))
    else:
        return str(field_spec(record))

def table(file, records, fields, headings, alignment = None):
    """
    Generate a Doxygen-flavor Markdown table from records.

    file -- Any object with a 'write' method that takes a single string
        parameter.
    records -- Iterable.  Rows will be generated from this.
    fields -- List of fields for each row.  Each entry may be an integer,
        string or a function.  If the entry is an integer, it is assumed to be
        an index of each record.  If the entry is a string, it is assumed to be
        a field of each record.  If the entry is a function, it is called with
        the record and its return value is taken as the value of the field.
    headings -- List of column headings.
    alignment - List of pairs alignment characters.  The first of the pair
        specifies the alignment of the header, (Doxygen won't respect this, but
        it might look good, the second specifies the alignment of the cells in
        the column.

        Possible alignment characters are:
            '<' = Left align (default for cells)
            '>' = Right align
            '^' = Center (default for column headings)
    """

    num_columns = len(fields)
    assert len(headings) == num_columns

    # Compute the table cell data
    columns = [[] for i in range(num_columns)]
    for record in records:
        for i, field in enumerate(fields):
            columns[i].append(evalute_field(record, field))

    # Fill out any missing alignment characters.
    extended_align = alignment if alignment != None else []
    if len(extended_align) > num_columns:
        extended_align = extended_align[0:num_columns]
    elif len(extended_align) < num_columns:
        extended_align += [('^', '<')
                           for i in range[num_columns-len(extended_align)]]

    heading_align, cell_align = [x for x in zip(*extended_align)]

    field_widths = [len(max(column, key=len)) if len(column) > 0 else 0
                    for column in columns]
    heading_widths = [max(len(head), 2) for head in headings]
    column_widths = [max(x) for x in zip(field_widths, heading_widths)]

    _ = ' | '.join(['{:' + a + str(w) + '}'
                    for a, w in zip(heading_align, column_widths)])
    heading_template = '| ' + _ + ' |'
    _ = ' | '.join(['{:' + a + str(w) + '}'
                    for a, w in zip(cell_align, column_widths)])
    row_template = '| ' + _ + ' |'

    _ = ' | '.join([left_rule[a] + '-'*(w-2) + right_rule[a]
                    for a, w in zip(cell_align, column_widths)])
    ruling = '| ' + _ + ' |'

    file.write(heading_template.format(*headings).rstrip() + '\n')
    file.write(ruling.rstrip() + '\n')
    for row in zip(*columns):
        file.write(row_template.format(*row).rstrip() + '\n')

这是一个简单的测试案例:

import sys

sys.stdout.write('State Capitals (source: Wikipedia)\n\n')

headings = ['State', 'Abrev.', 'Capital', 'Capital since', 'Population',
            'Largest Population?']

data = [('Alabama', 'AL', '1819', 'Montgomery', '1846', 155.4, False,
         205764),
        ('Alaska', 'AK', '1959', 'Juneau', '1906', 2716.7, False, 31275),
        ('Arizona', 'AZ', '1912', 'Phoenix', '1889',474.9, True, 1445632),
        ('Arkansas', 'AR', '1836', 'Little Rock', '1821', 116.2, True,
         193524)]

fields = [0, 1, 3, 4, 7, lambda rec: 'Yes' if rec[6] else 'No']

align = [('^', '<'), ('^', '^'), ('^', '<'), ('^', '^'), ('^', '>'),
         ('^','^')]

table(sys.stdout, data, fields, headings, align)

输出如下:

State Capitals (source: Wikipedia)

|  State   | Abrev. |   Capital   | Capital since | Population | Largest Population? |
| :------- | :----: | :---------- | :-----------: | ---------: | :-----------------: |
| Alabama  |   AL   | Montgomery  |     1846      |     205764 |         No          |
| Alaska   |   AK   | Juneau      |     1906      |      31275 |         No          |
| Arizona  |   AZ   | Phoenix     |     1889      |    1445632 |         Yes         |
| Arkansas |   AR   | Little Rock |     1821      |     193524 |         Yes         |

Doxygen 将其呈现为:

Sample


您的 table() 函数在使用 str() 时没有指定编码方式。当与 Unicode 输入数据一起使用时,它会引发 UnicodeEncodeErrors 错误。 - Alastair McCormack
@AlastairMcCormack,已经有一段时间了,但这一定是Python 2.7代码。 - Codie CodeMonkey
1
这更像是给其他人的一个提示 :) 最近我回答了一个人的问题,他引用了这个答案,但是使用的是Unicode字符串 :) - Alastair McCormack

1
我有一个要求,需要为最近的项目编写程序生成Markdown,因此我构建了一个库并将其发布在GitHub上。希望你会发现它有用。
该项目称为MarkdownLog,是一个轻量级(即最小依赖项),便携式.NET库(PCL),可以从.NET数据结构(如集合和字典)生成Markdown。我将其用于记录内部程序数据结构,以进行诊断目的,但它也应该满足您的需求。
以下是如何从集合构建Markdown表格的方法:
var data = new[]
{
    new{Year = 1991, Album = "Out of Time", Songs=11, Rating = "* * * *"},
    new{Year = 1992, Album = "Automatic for the People", Songs=12, Rating = "* * * * *"},
    new{Year = 1994, Album = "Monster", Songs=12, Rating = "* * *"}
};

Console.Write(data.ToMarkdownTable());

// Produces:
//
//     Year | Album                    | Songs | Rating   
//     ----:| ------------------------ | -----:| --------- 
//     1991 | Out of Time              |    11 | * * * *  
//     1992 | Automatic for the People |    12 | * * * * *
//     1994 | Monster                  |    12 | * * *    

请注意,当使用GitHub风格的Markdown解析器解析此输出时,将生成HTML表格。
默认情况下,列基于其数据类型对齐(数字右对齐,字符串左对齐),标题名称由对象的属性名称生成。如果这不是您想要的,有许多覆盖项可以更好地控制输出。
所有标准Markdown元素都内置支持,还有GFM表格。我还添加了一些我需要的额外元素类型(条形图,iOS UITableView),它们被实现为代码块,因此仍遵循Markdown标准。
我最近才将代码上传到GitHub,因此目前文档很简单。话虽如此,项目中有大量单元测试,应该能够演示它的工作原理。
我知道这个问题已经有一段时间了,但我希望这个项目对某些人有用。

1
我忘了说,我很乐意听取任何反馈或建议。 - Wheelie

0

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