優雅地使用 C++ 製做表格:tabulate

做者:HelloGitHub-ChungZHgit

博客:chungzh.cn/github

0x00 介紹 tabulate

tabulate 是一個使用 C++ 17 編寫的庫,它能夠製做表格。使用它,把表格對齊、格式化和着色,不在話下!你甚至可使用 tabulate,將你的表格導出爲 Markdown 代碼。下圖是一個使用 tabulate 製做的表格輸出在命令行的樣例:shell

固然,除了表格,你還能夠玩出花樣。看見下面這個馬里奧了嗎?這也是用 tabulate 製做的!源碼在 這裏bash

0x10 準備

首先你須要安裝 CMakemarkdown

建立一個文件夾(下文用 X 代替),做爲你使用 tabulate 的地方。再將 include 這個文件夾下載到 X 裏。而後在 X 裏建立 main.cpp 以及一個 CMakeLists.txtapp

注意:須要下載 include 整個文件夾而不是僅僅下載 tabulate 文件夾函數

你能夠點擊 這裏 下載 tabulate 項目,而後將 include 文件夾複製到 X 中。字體

將下面的代碼複製進 CMakeLists.txtui

cmake_minimum_required(VERSION 3.8)

# 這裏的 tabulateDemo 能夠換爲你喜歡的名字
project(tabulateDemo)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

include_directories(include)

add_executable(main main.cpp)
複製代碼

最後 X 文件夾的結構應該是這樣的:spa

.
├── CMakeLists.txt
├── include
│   └── tabulate
└── main.cpp
複製代碼

認真覈對好 X 的結構!

可前往 ChungZH/tabulatedemo 覈對文件結構。

0x20 小試身手

將下面這段代碼複製進 main.cpp 中:

#include "tabulate/table.hpp"
using namespace std;
using namespace tabulate;
int main() {
    Table hellogithub; // 建立一個叫作 hellogithub 的 Table 對象

    hellogithub.add_row({"HelloGitHub"}); 
    hellogithub.add_row({"hellogithub.com"});
    hellogithub[1][0].format()
        .font_style({FontStyle::underline});
    hellogithub.add_row({"github.com/521xueweihan/HelloGitHub"});
    hellogithub[2][0].format()
        .font_style({FontStyle::underline});
    hellogithub.add_row({"xueweihan NB!!!"});

    cout << hellogithub << endl;
    return 0;
}
複製代碼
  • 若是你使用的是 Linux/MacOS 系統,請在終端進入 X 文件夾並輸入如下命令:

    mkdir build
    cd build
    cmake ..
    make
    ./main
    複製代碼
  • 若是你使用的是 Windows 系統和 MinGW,請檢查是否安裝 mingw32-make.exe,並在終端中進入 X 文件夾,輸入:

    mkdir build
    cd build
    cmake ..
    mingw32-make
    ./main.exe
    複製代碼
  • 若是你使用 Windows 以及 MSVC,在終端中輸入:

    mkdir build
    cd build
    cmake ..
    複製代碼

而後使用 Visual Studio 打開 build 文件夾下的 tabulateDemo.sln 來運行。

若是沒有問題,那麼你應該會在終端裏看到:

0x30 格式化表格

請先認真分析 0x20 小試身手 章節中的代碼並嘗試着修改一下它!

0x31 Word Wrapping

爲了防止表格中的內容過長致使不整齊,你能夠指定表格每一列的寬度,tabulate 就會自動幫你換行。語法以下:

// 將表格第 0 行第 0 列的寬度設爲20
table[0][0].format().width(20);
複製代碼

除了自動換行,你也能夠在內容中使用 \n 來手動設置換行。

這是一個 Word Wrapping 的例子:

#include "tabulate/table.hpp"
using namespace std;
using namespace tabulate;
int main() {
    Table table;

    table.add_row({"This paragraph contains a veryveryveryveryveryverylong word. The long word will break and word wrap to the next line.",
                    "This paragraph \nhas embedded '\\n' \ncharacters and\n will break\n exactly where\n you want it\n to\n break."});

    table[0][0].format().width(20); // 設置第 0 行第 0 列的寬度爲 20
    table[0][1].format().width(50); // 設置第 0 行第 1 列的寬度爲 50

    cout << table << endl;
    return 0;
}
    return 0;
}
複製代碼
  • 第 0 行第 0 列的文字是否是很長?可是設置了它的寬度後,就不用擔憂了。tabulate 將會幫你自動換行。若是不設置的話,表格就會變得很不整齊,你也能夠嘗試一下。

  • 第 0 行第 1 列的內容裏運用了\n 的換行符,因此即便咱們給它設置了 50 的寬度,也會先根據內容裏的 \n 換行符來換行。

  • 值得注意的是,tabulate 會自動刪除每一行內容兩邊的空白字符。

0x32 字體對齊

tabulate 支持三種對齊設置:左、中和右。默認狀況下,所有內容都會靠左對齊。

要手動設置對齊方式,可使用 .format().font_align(方向)

舉一個例子:

#include "tabulate/table.hpp"
using namespace std;
using namespace tabulate;
int main() {
    Table hellogithub;

    hellogithub.add_row({"HelloGitHub"}); 
    hellogithub[0][0].format()
        .font_align(FontAlign::center); // 設置居中對齊
    hellogithub.add_row({"hellogithub.com"});
    hellogithub[1][0].format()
        .font_align(FontAlign::left); // 設置靠左對齊
    hellogithub.add_row({"github.com/521xueweihan/HelloGitHub"});
    hellogithub[2][0].format()
        .font_align(FontAlign::center); // 設置居中對齊
    hellogithub.add_row({"xueweihan NB!!!"});
    hellogithub[3][0].format()
        .font_align(FontAlign::right); // 設置靠右對齊

    hellogithub[0][0].format().width(50);
    cout << hellogithub << endl;
    return 0;
}
複製代碼

0x33 字體樣式

tabulate 支持如下八種字體樣式:

  • 粗體 bold

  • 深色 dark

  • 斜體 italic

  • 下劃線 underline

  • 閃爍 blink

  • 翻轉 reverse

  • 隱藏 concealed

  • 刪除線 crossed

某些樣式可能會由於終端的緣由而沒法顯示。

如:粗體、深色、斜體、閃爍等樣式,請慎用。

要使用這些樣式,能夠調用 .format().font_style({...})。樣式也能夠疊加使用。

0x34 顏色

你能夠對錶格的字體、邊框、角以及列分隔符號設置它們的前景或背景顏色。

tabulate 支持 8 種顏色:

  • 灰色 gray

  • 紅色 red

  • 綠色 green

  • 黃色 yellow

  • 藍色 blue

  • 洋紅色 magenta

  • 青色 cyan

  • 白色 white

能夠經過 .format().&lt;element&gt;_color(顏色) 的方式定義前景色或經過 .format().&lt;element&gt;_background_color(顏色) 的方式定義背景色。

#include "tabulate/table.hpp"
using namespace tabulate;
using namespace std;
int main() {
    Table colors;

    colors.add_row({"Font Color is Red", "Font Color is Blue", "Font Color is Green"});
    colors.add_row({"Everything is Red", "Everything is Blue", "Everything is Green"});
    colors.add_row({"Font Background is Red", "Font Background is Blue", "Font Background is Green"});

    colors[0][0].format()
        .font_color(Color::red)
        .font_style({FontStyle::bold});
    colors[0][1].format()
        .font_color(Color::blue)
        .font_style({FontStyle::bold});
    colors[0][2].format()
        .font_color(Color::green)
        .font_style({FontStyle::bold});

    colors[1][0].format()
        .border_left_color(Color::red)
        .border_left_background_color(Color::red)
        .font_background_color(Color::red)
        .font_color(Color::red);

    colors[1][1].format()
        .border_left_color(Color::blue)
        .border_left_background_color(Color::blue)
        .font_background_color(Color::blue)
        .font_color(Color::blue);

    colors[1][2].format()
        .border_left_color(Color::green)
        .border_left_background_color(Color::green)
        .font_background_color(Color::green)
        .font_color(Color::green)
        .border_right_color(Color::green)
        .border_right_background_color(Color::green);

    colors[2][0].format()
        .font_background_color(Color::red)
        .font_style({FontStyle::bold});
    colors[2][1].format()
        .font_background_color(Color::blue)
        .font_style({FontStyle::bold});
    colors[2][2].format()
        .font_background_color(Color::green)
        .font_style({FontStyle::bold});

    cout << colors << endl;
    return 0;
}
複製代碼

0x35 邊框、角

你能夠對錶格的邊框和角的文本、顏色或背景顏色進行自定義。

你可使用 .corner(..).corner_color(..)corner_background_color(..) 來對全部的角設置一個共同的樣式。你也可使用 .border(..) 、.border_color(..) 和 .border_background_color(..) 來對全部的邊框設置一個共同的樣式。

這是一個單獨設定全部邊框和角的示例:

#include <tabulate/table.hpp>
using namespace tabulate;

int main() {
  Table table;

  table.add_row({"ᛏᚺᛁᛊ ᛁᛊ ᚨ ᛊᛏᛟᚱy ᛟᚠᚨ ᛒᛖᚨᚱ ᚨᚾᛞ\n"
                 "ᚨ ᚹᛟᛚᚠ, ᚹᚺᛟ ᚹᚨᚾᛞᛖᚱᛖᛞ ᛏᚺᛖ\n"
                 "ᚱᛖᚨᛚᛗᛊ ᚾᛁᚾᛖ ᛏᛟ ᚠᚢᛚᚠᛁᛚᛚ ᚨ ᛈᚱᛟᛗᛁᛊᛖ\n"
                 "ᛏᛟ ᛟᚾᛖ ᛒᛖᚠᛟᚱᛖ; ᛏᚺᛖy ᚹᚨᛚᚲ ᛏᚺᛖ\n"
                 "ᛏᚹᛁᛚᛁᚷᚺᛏ ᛈᚨᛏᚺ, ᛞᛖᛊᛏᛁᚾᛖᛞ ᛏᛟ\n"
                 "ᛞᛁᛊcᛟᚹᛖᚱ ᛏᚺᛖ ᛏᚱᚢᛏᚺ\nᛏᚺᚨᛏ ᛁᛊ ᛏᛟ cᛟᛗᛖ."});

  table.format()
      .multi_byte_characters(true)
      // Font styling
      .font_style({FontStyle::bold, FontStyle::dark})
      .font_align(FontAlign::center)
      .font_color(Color::red)
      .font_background_color(Color::yellow)
      // Corners
      .corner_top_left("ᛰ")
      .corner_top_right("ᛯ")
      .corner_bottom_left("ᛮ")
      .corner_bottom_right("ᛸ")
      .corner_top_left_color(Color::cyan)
      .corner_top_right_color(Color::yellow)
      .corner_bottom_left_color(Color::green)
      .corner_bottom_right_color(Color::red)
      // Borders
      .border_top("ᛜ")
      .border_bottom("ᛜ")
      .border_left("ᚿ")
      .border_right("ᛆ")
      .border_left_color(Color::yellow)
      .border_right_color(Color::green)
      .border_top_color(Color::cyan)
      .border_bottom_color(Color::red);
  std::cout << table << std::endl;
  return 0;
}
複製代碼

0x36 基於範圍的迭代

一個一個設置表格的樣式是否是很麻煩?tabulate 提供了迭代器,支持對錶、行和列的迭代,更方便地格式化表格。

#include <tabulate/table.hpp>
using namespace tabulate;

int main() {
  Table table;

  table.add_row({"Company", "Contact", "Country"});
  table.add_row({"Alfreds Futterkiste", "Maria Anders", "Germany"});
  table.add_row({"Centro comercial Moctezuma", "Francisco Chang", "Mexico"});
  table.add_row({"Ernst Handel", "Roland Mendel", "Austria"});
  table.add_row({"Island Trading", "Helen Bennett", "UK"});
  table.add_row({"Laughing Bacchus Winecellars", "Yoshi Tannamuri", "Canada"});
  table.add_row({"Magazzini Alimentari Riuniti", "Giovanni Rovelli", "Italy"});

  // 設置每一行的寬度
  table.column(0).format().width(40);
  table.column(1).format().width(30);
  table.column(2).format().width(30);

  // 遍歷第一行中的單元格
  for (auto& cell : table[0]) {
    cell.format()
      .font_style({FontStyle::underline})
      .font_align(FontAlign::center);
  }

  // 遍歷第一列中的單元格
  for (auto& cell : table.column(0)) {
    if (cell.get_text() != "Company") {
      cell.format()
        .font_align(FontAlign::right);
    }
  }

  // 遍歷表格中的行
  size_t index = 0;
  for (auto& row : table) {
    row.format()
      .font_style({FontStyle::bold});

    // 輪流把整行的背景設爲藍色
    if (index > 0 && index % 2 == 0) {
      for (auto& cell : row) {
        cell.format()
          .font_background_color(Color::blue);
      }      
    }
    index += 1;
  }

  std::cout << table << std::endl;
}
複製代碼

0x37 嵌套表格

在 tabulate 中嵌套表格很容易,由於 Table.add_row(...) 這個函數能夠接受 std::string 類型和 tabulate::Table。下面是一個嵌套表格的例子:

#include "tabulate/table.hpp"
using namespace tabulate;
using namespace std;
int main() {
  Table hellogithub;
  hellogithub.add_row({"HelloGitHub"});
  hellogithub[0][0]
      .format()
      .font_background_color(Color::blue)
      .font_align(FontAlign::center);

  Table hglink;
  hglink.add_row({"GitHub repo", "Website"});
  hglink.add_row({"github.com/521xueweihan/HelloGitHub", "hellogithub.com"});

  hellogithub.add_row({hglink}); // 嵌套!
  cout << hellogithub << endl;
  return 0;
}
複製代碼

0x40 導出

0x41 Markdown

可使用 MarkdownExporter 來將一個表格導出爲 GFM 風格的 Markdown。

#include "tabulate/markdown_exporter.hpp" // 注意這個頭文件
#include "tabulate/table.hpp"
using namespace tabulate;
using namespace std;
int main() {
  Table hellogithub;
  hellogithub.add_row({"HelloGitHub"});
  hellogithub[0][0].format().font_style({FontStyle::bold}); // 加粗樣式,在 Markdown 中能夠表現出來
  hellogithub.add_row({"GitHub repo: github.com/521xueweihan/HelloGitHub"});
  hellogithub.add_row({"Website: hellogithub.com"});

  // 導出爲 Markdown
  MarkdownExporter exporter;
  auto markdown = exporter.dump(hellogithub);

  cout << hellogithub << endl << endl;
  cout << "Markdown Source:\n\n" << markdown << endl;
  return 0;
}
複製代碼

導出效果以下:

HelloGitHub
GitHub repo: github.com/521xueweihan/HelloGitHub
Website: hellogithub.com

注意:Markdown 不能指定每個單元格的對齊樣式,只能指定一列的對齊樣式,像這樣 hg.column(1).format().font_align(FontAlign::center);

0x50 尾聲

若是想要更詳細地瞭解 tabulate 的用法,請查看官方文檔 github.com/p-ranav/tab…

本文是做者的第一次關於此類型文章的嘗試,若有不足之處,請指正,謝謝!

再見!


關注公衆號加入交流羣

相關文章
相關標籤/搜索