zoukankan      html  css  js  c++  java
  • 优雅地使用 C++ 制作表格:tabulate

    作者:HelloGitHub-ChungZH

    0x00 介绍 tabulate

    tabulate 是一个使用 C++ 17 编写的库,它可以制作表格。使用它,把表格对齐、格式化和着色,不在话下!你甚至可以使用 tabulate,将你的表格导出为 Markdown 代码。下图是一个使用 tabulate 制作的表格输出在命令行的样例:

    当然,除了表格,你还可以玩出花样。看见下面这个马里奥了吗?这也是用 tabulate 制作的!源码在 这里

    0x10 准备

    首先你需要安装 CMake

    创建一个文件夹(下文用 X 代替),作为你使用 tabulate 的地方。再将 include 这个文件夹下载到 X 里。然后在 X 里创建 main.cpp 以及一个 CMakeLists.txt

    注意:需要下载 include 整个文件夹而不是仅仅下载 tabulate 文件夹

    你可以点击 这里 下载 tabulate 项目,然后将 include 文件夹复制到 X 中。

    将下面的代码复制进 CMakeLists.txt

    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 文件夹的结构应该是这样的:

    .
    ├── 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);
    

    除了自动换行,你也可以在内容中使用 来手动设置换行。

    这是一个 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 
    has embedded '\n' 
    characters and
     will break
     exactly where
     you want it
     to
     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 列的内容里运用了 的换行符,所以即使我们给它设置了 50 的宽度,也会先根据内容里的 换行符来换行。

    • 值得注意的是,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 ᛟᚠᚨ ᛒᛖᚨᚱ ᚨᚾᛞ
    "
                     "ᚨ ᚹᛟᛚᚠ, ᚹᚺᛟ ᚹᚨᚾᛞᛖᚱᛖᛞ ᛏᚺᛖ
    "
                     "ᚱᛖᚨᛚᛗᛊ ᚾᛁᚾᛖ ᛏᛟ ᚠᚢᛚᚠᛁᛚᛚ ᚨ ᛈᚱᛟᛗᛁᛊᛖ
    "
                     "ᛏᛟ ᛟᚾᛖ ᛒᛖᚠᛟᚱᛖ; ᛏᚺᛖy ᚹᚨᛚᚲ ᛏᚺᛖ
    "
                     "ᛏᚹᛁᛚᛁᚷᚺᛏ ᛈᚨᛏᚺ, ᛞᛖᛊᛏᛁᚾᛖᛞ ᛏᛟ
    "
                     "ᛞᛁᛊcᛟᚹᛖᚱ ᛏᚺᛖ ᛏᚱᚢᛏᚺ
    ᛏᚺᚨᛏ ᛁᛊ ᛏᛟ 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:
    
    " << 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 的用法,请查看官方文档 https://github.com/p-ranav/tabulate

    本文是作者的第一次关于此类型文章的尝试,如有不足之处,请指正,谢谢!

    再见!


    关注公众号加入交流群

  • 相关阅读:
    UVa 1151 Buy or Build【最小生成树】
    UVa 216 Getting in Line【枚举排列】
    UVa 729 The Hamming Distance Problem【枚举排列】
    HDU 5214 Movie【贪心】
    HDU 5223 GCD
    POJ 1144 Network【割顶】
    UVa 11025 The broken pedometer【枚举子集】
    HDU 2515 Yanghee 的算术【找规律】
    Java基本语法
    Java环境变量,jdk和jre的区别,面向对象语言编程
  • 原文地址:https://www.cnblogs.com/xueweihan/p/12357404.html
Copyright © 2011-2022 走看看