TextTable

Swift 5.0 License MIT License MIT Build Status

易于在 Swift 中打印文本表格。灵感来源于 Python 的 tabulate 库。

即将到来的变更

查看即将到来的变更的详细信息。

特性

要求

此软件包是为以下 Swift 版本编写和测试的

Apple Swift 版本 5.0

用法

使用 Swift Package Manager 安装 TextTable 作为您项目的依赖项。

dependencies: [
    .package(
        url: "https://github.com/cfilipov/TextTable",
        .branch("master"))
]

首先,导入 TextTable

import TextTable

在所有示例中,将使用以下数据结构

struct Person {
    let name: String
    let age: Int
    let birhtday: Date
}

假设我们有一个 Person 集合,我们希望将其显示为表格。

let data = [
	Person(name: "Alice", age: 42, birhtday: Date()),
	Person(name: "Bob", age: 22, birhtday: Date()),
	Person(name: "Eve", age: 142, birhtday: Date())
]

配置 TextTable 实例

为您希望打印为表格的类型配置 TextTable 实例。最小配置将需要将值映射到您类型的列。

let table = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age),
     Column("Birthday" <- $0.birhtday)]
}

备选语法

<- 运算符只是语法糖。如果您不想使用该运算符,可以使用带标签的参数代替。

let table = TextTable<Person> {
    [Column(title: "Name", value: $0.name),
     Column(title: "Age", value: $0.age),
     Column(title: "Birthday", value: $0.birhtday)]
}

打印表格

只需调用 print() 方法,传入 Person 的集合。

table.print(data)
Name  Age Birthday
----- --- -------------------------
Alice 42  2016-08-13 19:21:54 +0000
Bob   22  2016-08-13 19:21:54 +0000
Eve   142 2016-08-13 19:21:54 +0000

获取字符串

您也可以通过调用 string(for:) 方法来获取渲染后的表格字符串。

let s = table.string(for: data)

自定义表格样式

您可以选择使用 style: 参数指定表格样式。

table.print(data, style: Style.psql)
+-------+-----+---------------------------+
| Name  | Age | Birthday                  |
+-------+-----+---------------------------+
| Alice | 42  | 2016-08-13 08:12:58 +0000 |
| Bob   | 22  | 2016-08-13 08:12:58 +0000 |
| Eve   | 142 | 2016-08-13 08:12:58 +0000 |
+-------+-----+---------------------------+

在使用 string(for:) 方法时也可以指定样式。

let s = table.string(for: data, style: Style.psql)

有关支持样式的完整列表,请参见下方的支持的样式

列特定的格式化器

您可以为每列指定一个 Formatter。在此示例中,使用 DateFormatter 来自定义 Birthday 列的显示。

let df = DateFormatter()
dateFormatter.dateStyle = .medium

let table = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age),
     Column("Birthday" <- $0.birhtday, formatter: df)]
}

table.print(data, style: Style.psql)
+-------+-----+--------------+
| Name  | Age | Birthday     |
+-------+-----+--------------+
| Alice | 42  | Aug 13, 2016 |
| Bob   | 22  | Aug 13, 2016 |
| Eve   | 142 | Aug 13, 2016 |
+-------+-----+--------------+

列宽

默认情况下,TextTable 将计算每列所需的宽度。您也可以使用 width: 参数显式设置宽度。

let table = TextTable<Person> {
    [Column("Name" <- $0.name, width: 10),
     Column("Age" <- $0.age, width: 10)]
}

table.print(data, style: Style.psql)
+------------+------------+
| Name       | Age        |
+------------+------------+
| Alice      | 42         |
| Bob        | 22         |
| Eve        | 142        |
+------------+------------+

关于填充的说明

某些表格样式可能包含内容左侧和/或右侧的填充,宽度参数不影响此填充。

截断

截断尾部

默认情况下,如果指定了宽度并且列的内容比宽度更宽,则文本将在尾部被截断(.tail 截断模式)。

let table = TextTable<Person> {
	[Column("Name" <- $0.name, width: 4), // defaults to truncation: .tail
	 Column("Age" <- $0.age)]
}

table.print(data, style: testStyle)
+------+-----+
| Name | Age |
+------+-----+
| Ali… | 42  |
| Bob  | 22  |
| Eve  | 142 |
+------+-----+

截断头部

您还可以通过为 truncation: 参数指定 .head 来在文本字符串的头部截断。如果不存在 width 参数,则 truncation 参数无效。

let table = TextTable<Person> {
	[Column("Name" <- $0.name, width: 4, truncate: .head),
	 Column("Age" <- $0.age)]
}

table.print(data, style: testStyle)
+------+-----+
| Name | Age |
+------+-----+
| …ice | 42  |
| Bob  | 22  |
| Eve  | 142 |
+------+-----+

截断错误

如果您希望在列的内容不符合指定的宽度时触发致命错误,则可以指定 .error 截断模式。

let table = TextTable<Person> {
	[Column("Name" <- $0.name, width: 4, truncate: .error),
	 Column("Age" <- $0.age)]
}

table.print(data, style: testStyle)

// fatal error: Truncation error

列对齐

默认情况下,所有列都将左对齐。您可以单独设置每列的对齐方式。

let table = TextTable<Person> {
    [Column("Name" <- $0.name), // default: .left
     Column("Age" <- $0.age, align: .right)]
}

table.print(data, style: Style.psql)
+-------+-----+
| Name  | Age |
+-------+-----+
| Alice |  42 |
| Bob   |  22 |
| Eve   | 142 |
+-------+-----+

性能特征

默认情况下,TextTable 将计算未使用 width: 列参数指定显式宽度的每列的必要宽度。此计算通过迭代表格的每一行两次来完成:第一次计算最大宽度,第二次实际渲染表格。对于非常大的表格,此计算的开销可能不合适。如果您希望避免此计算,则必须为每列指定宽度。

在下面的示例中,table1 将产生宽度计算的开销,因为其中一列没有显式宽度。另一方面,table2 不会产生开销,因为所有列都具有显式宽度。

// This will still result in extra calculations
let table1 = TextTable<Person> {
    [Column("Name" <- $0.name),
     Column("Age" <- $0.age, width: 10)]
}
// This will skip the width calculations
let table2 = TextTable<Person> {
    [Column("Name" <- $0.name, width: 9),
     Column("Age" <- $0.age, width: 10)]
}

支持的样式

TextTable 支持与 tabulate 大部分相同的样式。

Plain

table.print(data, style: Style.plain)
Name  Age Birthday
Alice  42  8/13/16
Bob    22  8/13/16
Eve   142  8/13/16

Simple

simple 是默认样式。它对应于 Pandoc Markdown 扩展中的 simple_tables

table.print(data) // or:
table.print(data, style: Style.simple)
Name  Age Birthday
----- --- --------
Alice  42  8/13/16
Bob    22  8/13/16
Eve   142  8/13/16

Grid

grid 遵循 Emacs 的 table.el package 的约定。它对应于 Pandoc Markdown 扩展中的 grid_tables

table.print(data, style: Style.grid)
+-------+-----+----------+
| Name  | Age | Birthday |
+=======+=====+==========+
| Alice |  42 |  8/13/16 |
+-------+-----+----------+
| Bob   |  22 |  8/13/16 |
+-------+-----+----------+
| Eve   | 142 |  8/13/16 |
+-------+-----+----------+

FancyGrid

table.print(data, style: Style.fancyGrid)
╒═══════╤═════╤══════════╕
│ Name  │ Age │ Birthday │
╞═══════╪═════╪══════════╡
│ Alice │  42 │  8/13/16 │
├───────┼─────┼──────────┤
│ Bob   │  22 │  8/13/16 │
├───────┼─────┼──────────┤
│ Eve   │ 142 │  8/13/16 │
╘═══════╧═════╧══════════╛

Psql

table.print(data, style: Style.psql)
+-------+-----+----------+
| Name  | Age | Birthday |
+-------+-----+----------+
| Alice |  42 |  8/13/16 |
| Bob   |  22 |  8/13/16 |
| Eve   | 142 |  8/13/16 |
+-------+-----+----------+

Pipe

pipe 遵循 PHP Markdown Extra extension 的约定。它对应于 Pandoc 中的 pipe_tables。此样式使用冒号来指示列对齐方式。

table.print(data, style: Style.pipe)
| Name  | Age | Birthday |
|:------|----:|---------:|
| Alice |  42 |  8/13/16 |
| Bob   |  22 |  8/13/16 |
| Eve   | 142 |  8/13/16 |

Org

org 遵循 Emacs org-mode 的约定。

table.print(data, style: Style.org)
| Name  | Age | Birthday |
|-------+-----+----------|
| Alice |  42 |  8/13/16 |
| Bob   |  22 |  8/13/16 |
| Eve   | 142 |  8/13/16 |

Rst

rst 遵循 reStructuredText 样式的简单表格的约定。

table.print(data, style: Style.rst)
===== === ========
Name  Age Birthday
===== === ========
Alice  42  8/13/16
Bob    22  8/13/16
Eve   142  8/13/16
===== === ========

Html

html 生成 HTML 表格 标记。

table.print(data, style: Style.html)
<table>
    <tr>
        <th style="text-align:left;">Name</th>
        <th style="text-align:center;">Age</th>
        <th style="text-align:right;">Birthday</th>
    </tr>
    <tr>
        <td>Alice</td>
        <td>42</td>
        <td>8/14/16</td>
    </tr>
    <tr>
        <td>Bob</td>
        <td>22</td>
        <td>8/14/16</td>
    </tr>
    <tr>
        <td>Eve</td>
        <td>142</td>
        <td>8/14/16</td>
    </tr>
</table>

Latex

latexLatex 生成 tabular 环境。

table.print(data, style: Style.latex)
\begin{tabular}{lll}
\hline
 Name & Age & Birthday \\
\hline
 Alice & 42 & 8/13/16 \\
 Bob & 22 & 8/13/16 \\
 Eve & 142 & 8/13/16 \\
\hline
\end{tabular}

自定义样式

您可以通过遵循 TextTableStyle 协议来创建自定义表格样式。

enum MyCustomStyle: TextTableStyle {
    // implement methods from TextTableStyle...
}

extension Style {
    static let custom = MyCustomStyle.self
}

table.print(data, style: Style.custom)

变更

更改列表可以在 CHANGELOG 中找到。

备选方案

提供类似用途的其他 Swift 库。

许可证

MIT 许可证

版权所有 (c) 2016 Cristian Filipov

特此授予许可,免费向任何获得本软件和相关文档文件(“软件”)副本的人员授予许可,以处理本软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,并允许向为此目的提供本软件的人员提供以下条件

上述版权声明和本许可声明应包含在本软件的所有副本或主要部分中。

本软件按“原样”提供,不提供任何形式的明示或暗示的保证,包括但不限于适销性、特定用途的适用性和非侵权性的保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是在合同诉讼、侵权诉讼或其他诉讼中,由本软件或本软件的使用或其他交易引起、产生或与之相关。