Formátovaný tisk obsahu tabulek a dalších datových struktur v Go

9. 3. 2021
Doba čtení: 24 minut

Sdílet

 Autor: Go lang
Dnes navážeme s popisem knihoven tabwriter, tablewriter a tableprinter určených pro tisk tabulek v programovacím jazyku Go. Dokončíme ukázky využití knihovny tablewriter a zaměříme se na knihovnu s mnohem většími možnostmi: go-pretty.

Obsah

1. Formátovaný tisk obsahu tabulek a dalších datových struktur

2. Načtení tabulky z formátu CSV balíčkem olekukonko/tablewriter

3. Nastavení zarovnání hodnot ve sloupcích

4. Tabulka vložitelná do souboru ve formátu Markdown

5. Horizontální oddělovače mezi řádky tabulky

6. Automatické spojení buněk se shodným obsahem

7. Specifikace sloupců, ve kterých má ke spojení buněk dojít

8. Získání řetězce reprezentujícího celou vykreslenou tabulku

9. Barevný výstup

10. Nepracuje se pouze s tabulkami: pokročilá knihovna go-pretty

11. Zobrazení jednoduché tabulky knihovnou go-pretty

12. Patička tabulky

13. Styl vykreslení tabulek

14. Ostatní styly okrajů tabulek

15. Styly se změnou barvy tabulky či její části

16. Ostatní připravené barevné styly

17. Obsah následující části seriálu

18. Příloha: často používané formáty tabulek vykreslených neproporcionálním písmem

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Formátovaný tisk obsahu tabulek a dalších datových struktur

V předchozím článku jsme se seznámili s několika možnostmi formátování tabulek (resp. přesněji řečeno dat organizovaných do formy dvojrozměrné tabulky) tak, aby se naformátovaná tabulka mohla zobrazit na běžném terminálu (konzoli) s neproporcionálním fontem. Připomeňme si ve stručnosti, že popsáno bylo hned několik balíčků určených pro programovací jazyk Go, které tvorbu naformátovaných tabulek podporují. Především se jedná o standardní balíček text/tabwriter, který dokáže naformátovat prakticky jakýkoli text, v němž jsou jednotlivé buňky výsledné tabulky ukončeny (nikoli ovšem rozděleny) zadaným znakem, jímž je typicky znak Tab (v řetězci je reprezentován symbolem ‚\t‘. Tento standardní balíček má poměrně široké možnosti využití, ovšem nemusí se hodit pro všechny situace. Na druhou stranu je jeho použití triviální a spočívá (poněkud zjednodušeně řečeno) v náhradě funkcí fmt.Print??? za funkce tabwriter.Fprint???, a to z toho důvodu, že balíček text/tabwriter splňuje standardní rozhraní io.Writer.

Příklad zobrazení tabulky tímto balíčkem. Povšimněte si možnosti specifikace vodicích znaků:

1..........1..........1
22.........22.........22
333........333........333
4444.......4444.......4444

Další způsob zobrazení tabulky:

    N1|    N2|    N3|
     1|     1|     1|
    22|    22|    22|
   333|   333|   333|
  4444|  4444|  4444|

Minule jsme se, i když prozatím ve stručnosti, věnovali i dalším dvěma balíčkům nazvaným tablewriter a tableprinter. Základem je balíček tablewriter, resp. přesněji olekukonko/tablewriter, jenž umožňuje tabulku nejdříve postupně zkonstruovat z jednotlivých řádků (metoda Append) a posléze celou tabulku naformátovat a zobrazit metodou Render. Viz též následující demonstrační příklad:

package main
 
import (
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        data := [][]string{
                []string{"A", "The Good", "500"},
                []string{"B", "The Very very Bad Man", "288"},
                []string{"C", "The Ugly", "120"},
                []string{"D", "The Gopher", "800"},
        }
 
        table := tablewriter.NewWriter(os.Stdout)
        table.SetHeader([]string{"Name", "Sign", "Rating"})
 
        for _, v := range data {
                table.Append(v)
        }
        table.Render()
}

Výsledná tabulka vypadá značně odlišně od předchozích tabulek, mj. i díky tomu, že obsahuje hlavičku, patičku, okraje sloupců atd:

+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
| A    | The Good              |    500 |
| B    | The Very very Bad Man |    288 |
| C    | The Ugly              |    120 |
| D    | The Gopher            |    800 |
+------+-----------------------+--------+
|                 SUM          |  1708  |
+------+-----------------------+--------+

Další možnosti nabízené knihovnou tablewriter jsou ukázány v navazujících kapitolách.

2. Načtení tabulky z formátu CSV balíčkem olekukonko/tablewriter

Knihovna tablewriter dokáže načítat data ve formátu CSV, a to jak tabulky bez hlavičky, tak i tabulky s hlavičkou. Pro otestování této možnosti použijeme CSV soubor s následujícím obsahem:

"Sep 2019","Change","Language","Ratings","Changep"
"2","change","C","15.95","0.74"
"1","change","Java","13.48","-3.18"
"3","","Python","10.47","0.59"
"4","","C++","7.11","1.48"
"5","","C#","4.58","1.18"
"6","","Visual Basic","4.12","0.83"
"7","","JavaScript","2.54","0.41"
"9","change","PHP","2.49","0.62"
"19","change","R","2.37","1.33"
"8","change","SQL","1.76","-0.19"
"14","change","Go","1.46","0.24"
"16","change","Swift","1.38","0.28"
"20","change","Perl","1.30","0.26"
"12","change","Assembly language","1.30","-0.08"
"15","","Ruby","1.24","0.03"

V dalším příkladu je ukázáno, jak se takový soubor načte: použije se odlišný konstruktor tabulky nazvaný NewCSV, kterému se předá jak objekt splňující rozhraní io.Writer, tak i jméno tabulky a příznak, zda tabulka obsahuje hlavičky:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", false)
        if err != nil {
                log.Fatal(err)
        }
        table.Render()
}

Výsledkem je tabulka neobsahující hlavičku:

+----------+--------+-------------------+---------+---------+
| Sep 2019 | Change | Language          | Ratings | Changep |
|        2 | change | C                 |   15.95 |    0.74 |
|        1 | change | Java              |   13.48 |   -3.18 |
|        3 |        | Python            |   10.47 |    0.59 |
|        4 |        | C++               |    7.11 |    1.48 |
|        5 |        | C#                |    4.58 |    1.18 |
|        6 |        | Visual Basic      |    4.12 |    0.83 |
|        7 |        | JavaScript        |    2.54 |    0.41 |
|        9 | change | PHP               |    2.49 |    0.62 |
|       19 | change | R                 |    2.37 |    1.33 |
|        8 | change | SQL               |    1.76 |   -0.19 |
|       14 | change | Go                |    1.46 |    0.24 |
|       16 | change | Swift             |    1.38 |    0.28 |
|       20 | change | Perl              |    1.30 |    0.26 |
|       12 | change | Assembly language |    1.30 |   -0.08 |
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

Načtení tabulky s hlavičkou se liší pouze v podržené části kódu:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.Render()
}

Nyní je již výsledek zcela korektní:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
|        1 | change | Java              |   13.48 |   -3.18 |
|        3 |        | Python            |   10.47 |    0.59 |
|        4 |        | C++               |    7.11 |    1.48 |
|        5 |        | C#                |    4.58 |    1.18 |
|        6 |        | Visual Basic      |    4.12 |    0.83 |
|        7 |        | JavaScript        |    2.54 |    0.41 |
|        9 | change | PHP               |    2.49 |    0.62 |
|       19 | change | R                 |    2.37 |    1.33 |
|        8 | change | SQL               |    1.76 |   -0.19 |
|       14 | change | Go                |    1.46 |    0.24 |
|       16 | change | Swift             |    1.38 |    0.28 |
|       20 | change | Perl              |    1.30 |    0.26 |
|       12 | change | Assembly language |    1.30 |   -0.08 |
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

3. Nastavení zarovnání hodnot ve sloupcích

Z výsledku vytištěného příkladem z předchozí kapitoly je patrné, že textové hodnoty jsou implicitně zarovnány doleva a číselné hodnoty doprava. Ovšem zarovnání je možné v případě potřeby specifikovat explicitně – ovšem pro tabulku jako celek. To je ukázáno na dalším příkladu:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetAlignment(tablewriter.ALIGN_LEFT)
        table.Render()
}

S výsledkem, který sice odpovídá kódu, ale není tak přehledný, jako předchozí výsledek:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
| 2        | change | C                 | 15.95   | 0.74    |
| 1        | change | Java              | 13.48   | -3.18   |
| 3        |        | Python            | 10.47   | 0.59    |
| 4        |        | C++               | 7.11    | 1.48    |
| 5        |        | C#                | 4.58    | 1.18    |
| 6        |        | Visual Basic      | 4.12    | 0.83    |
| 7        |        | JavaScript        | 2.54    | 0.41    |
| 9        | change | PHP               | 2.49    | 0.62    |
| 19       | change | R                 | 2.37    | 1.33    |
| 8        | change | SQL               | 1.76    | -0.19   |
| 14       | change | Go                | 1.46    | 0.24    |
| 16       | change | Swift             | 1.38    | 0.28    |
| 20       | change | Perl              | 1.30    | 0.26    |
| 12       | change | Assembly language | 1.30    | -0.08   |
| 15       |        | Ruby              | 1.24    | 0.03    |
+----------+--------+-------------------+---------+---------+

Zarovnání doprava:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetAlignment(tablewriter.ALIGN_RIGHT)
        table.Render()
}

S výsledkem:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change |                 C |   15.95 |    0.74 |
|        1 | change |              Java |   13.48 |   -3.18 |
|        3 |        |            Python |   10.47 |    0.59 |
|        4 |        |               C++ |    7.11 |    1.48 |
|        5 |        |                C# |    4.58 |    1.18 |
|        6 |        |      Visual Basic |    4.12 |    0.83 |
|        7 |        |        JavaScript |    2.54 |    0.41 |
|        9 | change |               PHP |    2.49 |    0.62 |
|       19 | change |                 R |    2.37 |    1.33 |
|        8 | change |               SQL |    1.76 |   -0.19 |
|       14 | change |                Go |    1.46 |    0.24 |
|       16 | change |             Swift |    1.38 |    0.28 |
|       20 | change |              Perl |    1.30 |    0.26 |
|       12 | change | Assembly language |    1.30 |   -0.08 |
|       15 |        |              Ruby |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

4. Tabulka vložitelná do souboru ve formátu Markdown

Často se setkáme s požadavkem na vytvoření tabulky ve formátech podporovaných dalšími různými nástroji. Některé často používané formáty tabulek jsou uvedeny v osmnácté kapitole. Zde si ukážeme, jak lze specifikací okrajů i způsobů zobrazení oddělovačů na rozích buněk vytvořit tabulku ve formátu, který je kompatibilní s některými realizacemi Markdownu (i když samotný Markdown tabulky oficiálně nepodporuje, některá jeho rozšíření ano):

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
        table.SetCenterSeparator("|")
        table.Render()
}

V příkladu bylo specifikováno, které okraje se mají zobrazit. Dále jsme uvedli znak použitý v místě, kde se střetávají rohy buněk (což může být zobrazeno odlišně od samotných okrajů):

| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
|----------|--------|-------------------|---------|---------|
|        2 | change | C                 |   15.95 |    0.74 |
|        1 | change | Java              |   13.48 |   -3.18 |
|        3 |        | Python            |   10.47 |    0.59 |
|        4 |        | C++               |    7.11 |    1.48 |
|        5 |        | C#                |    4.58 |    1.18 |
|        6 |        | Visual Basic      |    4.12 |    0.83 |
|        7 |        | JavaScript        |    2.54 |    0.41 |
|        9 | change | PHP               |    2.49 |    0.62 |
|       19 | change | R                 |    2.37 |    1.33 |
|        8 | change | SQL               |    1.76 |   -0.19 |
|       14 | change | Go                |    1.46 |    0.24 |
|       16 | change | Swift             |    1.38 |    0.28 |
|       20 | change | Perl              |    1.30 |    0.26 |
|       12 | change | Assembly language |    1.30 |   -0.08 |
|       15 |        | Ruby              |    1.24 |    0.03 |

Odlišné nastavení:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetBorders(tablewriter.Border{Left: false, Top: true, Right: false, Bottom: true})
        table.SetCenterSeparator("*")
        table.Render()
}

S výsledkem:

-----------*--------*-------------------*---------*----------
  SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP
-----------*--------*-------------------*---------*----------
         2 | change | C                 |   15.95 |    0.74
         1 | change | Java              |   13.48 |   -3.18
         3 |        | Python            |   10.47 |    0.59
         4 |        | C++               |    7.11 |    1.48
         5 |        | C#                |    4.58 |    1.18
         6 |        | Visual Basic      |    4.12 |    0.83
         7 |        | JavaScript        |    2.54 |    0.41
         9 | change | PHP               |    2.49 |    0.62
        19 | change | R                 |    2.37 |    1.33
         8 | change | SQL               |    1.76 |   -0.19
        14 | change | Go                |    1.46 |    0.24
        16 | change | Swift             |    1.38 |    0.28
        20 | change | Perl              |    1.30 |    0.26
        12 | change | Assembly language |    1.30 |   -0.08
        15 |        | Ruby              |    1.24 |    0.03
-----------*--------*-------------------*---------*----------

5. Horizontální oddělovače mezi řádky tabulky

V případě, že je tabulka krátká, popř. se používá terminál s volným místem ve vertikálním směru (ovšem tohoto místa je vždy málo), je možné mezi jednotlivé řádky tabulky vložit horizontální oddělovače. Ty se zapnou metodou SetRowLine tak, jak je to ukázáno v dalším demonstračním příkladu:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetRowLine(true)
        table.Render()
}

Výsledkem je tabulka se stejnými údaji, jako je tomu v předchozích příkladech, ovšem zabírající prakticky dvojnásobnou plochu:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
+----------+--------+-------------------+---------+---------+
|        1 | change | Java              |   13.48 |   -3.18 |
+----------+--------+-------------------+---------+---------+
|        3 |        | Python            |   10.47 |    0.59 |
+----------+--------+-------------------+---------+---------+
|        4 |        | C++               |    7.11 |    1.48 |
+----------+--------+-------------------+---------+---------+
|        5 |        | C#                |    4.58 |    1.18 |
+----------+--------+-------------------+---------+---------+
|        6 |        | Visual Basic      |    4.12 |    0.83 |
+----------+--------+-------------------+---------+---------+
|        7 |        | JavaScript        |    2.54 |    0.41 |
+----------+--------+-------------------+---------+---------+
|        9 | change | PHP               |    2.49 |    0.62 |
+----------+--------+-------------------+---------+---------+
|       19 | change | R                 |    2.37 |    1.33 |
+----------+--------+-------------------+---------+---------+
|        8 | change | SQL               |    1.76 |   -0.19 |
+----------+--------+-------------------+---------+---------+
|       14 | change | Go                |    1.46 |    0.24 |
+----------+--------+-------------------+---------+---------+
|       16 | change | Swift             |    1.38 |    0.28 |
+----------+--------+-------------------+---------+---------+
|       20 | change | Perl              |    1.30 |    0.26 |
+----------+--------+-------------------+---------+---------+
|       12 | change | Assembly language |    1.30 |   -0.08 |
+----------+--------+-------------------+---------+---------+
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

6. Automatické spojení buněk se shodným obsahem

Sousední buňky se shodným obsahem, což je ostatně poměrně častý případ, se mohou automaticky spojit. Pro tento účel se používá metoda pojmenovaná SetAutoMergeCells tak, jak je to ukázáno v tomto příkladu:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetAutoMergeCells(true)
        table.SetRowLine(true)
        table.Render()
}

Výsledkem je tato tabulka:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
+----------+        +-------------------+---------+---------+
|        1 |        | Java              |   13.48 |   -3.18 |
+----------+--------+-------------------+---------+---------+
|        3 |        | Python            |   10.47 |    0.59 |
+----------+--------+-------------------+---------+---------+
|        4 |        | C++               |    7.11 |    1.48 |
+----------+--------+-------------------+---------+---------+
|        5 |        | C#                |    4.58 |    1.18 |
+----------+--------+-------------------+---------+---------+
|        6 |        | Visual Basic      |    4.12 |    0.83 |
+----------+--------+-------------------+---------+---------+
|        7 |        | JavaScript        |    2.54 |    0.41 |
+----------+--------+-------------------+---------+---------+
|        9 | change | PHP               |    2.49 |    0.62 |
+----------+        +-------------------+---------+---------+
|       19 |        | R                 |    2.37 |    1.33 |
+----------+        +-------------------+---------+---------+
|        8 |        | SQL               |    1.76 |   -0.19 |
+----------+        +-------------------+---------+---------+
|       14 |        | Go                |    1.46 |    0.24 |
+----------+        +-------------------+---------+---------+
|       16 |        | Swift             |    1.38 |    0.28 |
+----------+        +-------------------+---------+---------+
|       20 |        | Perl              |    1.30 |    0.26 |
+----------+        +-------------------+         +---------+
|       12 |        | Assembly language |         |   -0.08 |
+----------+--------+-------------------+---------+---------+
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

7. Specifikace sloupců, ve kterých má ke spojení buněk dojít

Automatické spojování sousedních buněk se shodným obsahem je možné omezit pouze na vybrané sloupce. K tomuto účelu slouží metoda pojmenovaná SetAutoMergeCellsByColumnIndex, které se předává pole, resp. řez s indexy sloupců, jichž se má spojování týkat. Jak je v programovacím jazyce Go zvykem, jsou sloupce číslovány od jedničky (na rozdíl od tabulkových procesorů):

package main
 
import (
        "fmt"
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetRowLine(true)
        table.SetAutoMergeCellsByColumnIndex([]int{1, 2})
        table.Render()
 
        fmt.Println()
        fmt.Println()
 
        table.SetAutoMergeCellsByColumnIndex([]int{3})
        table.Render()
}

Výše uvedený demonstrační příklad po svém spuštění zobrazí dvojici tabulek, pokaždé s jinými buňkami, které jsou sloučeny:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
+----------+        +-------------------+---------+---------+
|        1 |        | Java              |   13.48 |   -3.18 |
+----------+--------+-------------------+---------+---------+
|        3 |        | Python            |   10.47 |    0.59 |
+----------+--------+-------------------+---------+---------+
|        4 |        | C++               |    7.11 |    1.48 |
+----------+--------+-------------------+---------+---------+
|        5 |        | C#                |    4.58 |    1.18 |
+----------+--------+-------------------+---------+---------+
|        6 |        | Visual Basic      |    4.12 |    0.83 |
+----------+--------+-------------------+---------+---------+
|        7 |        | JavaScript        |    2.54 |    0.41 |
+----------+--------+-------------------+---------+---------+
|        9 | change | PHP               |    2.49 |    0.62 |
+----------+        +-------------------+---------+---------+
|       19 |        | R                 |    2.37 |    1.33 |
+----------+        +-------------------+---------+---------+
|        8 |        | SQL               |    1.76 |   -0.19 |
+----------+        +-------------------+---------+---------+
|       14 |        | Go                |    1.46 |    0.24 |
+----------+        +-------------------+---------+---------+
|       16 |        | Swift             |    1.38 |    0.28 |
+----------+        +-------------------+---------+---------+
|       20 |        | Perl              |    1.30 |    0.26 |
+----------+        +-------------------+---------+---------+
|       12 |        | Assembly language |    1.30 |   -0.08 |
+----------+--------+-------------------+---------+---------+
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+
 
 
+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
+----------+--------+-------------------+---------+---------+
|        1 | change | Java              |   13.48 |   -3.18 |
+----------+--------+-------------------+---------+---------+
|        3 |        | Python            |   10.47 |    0.59 |
+----------+--------+-------------------+---------+---------+
|        4 |        | C++               |    7.11 |    1.48 |
+----------+--------+-------------------+---------+---------+
|        5 |        | C#                |    4.58 |    1.18 |
+----------+--------+-------------------+---------+---------+
|        6 |        | Visual Basic      |    4.12 |    0.83 |
+----------+--------+-------------------+---------+---------+
|        7 |        | JavaScript        |    2.54 |    0.41 |
+----------+--------+-------------------+---------+---------+
|        9 | change | PHP               |    2.49 |    0.62 |
+----------+--------+-------------------+---------+---------+
|       19 | change | R                 |    2.37 |    1.33 |
+----------+--------+-------------------+---------+---------+
|        8 | change | SQL               |    1.76 |   -0.19 |
+----------+--------+-------------------+---------+---------+
|       14 | change | Go                |    1.46 |    0.24 |
+----------+--------+-------------------+---------+---------+
|       16 | change | Swift             |    1.38 |    0.28 |
+----------+--------+-------------------+---------+---------+
|       20 | change | Perl              |    1.30 |    0.26 |
+----------+--------+-------------------+         +---------+
|       12 | change | Assembly language |         |   -0.08 |
+----------+--------+-------------------+---------+---------+
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

8. Získání řetězce reprezentujícího celou vykreslenou tabulku

Podívejme se nyní na další potenciálně užitečnou vlastnost knihovny tablewriter. Relativně snadno totiž můžeme získat řetězec s obsahem celé vykreslené tabulky. Jinými slovy – tabulku není nutné pouze vytisknout, ale získat lze i její podobu. Toto chování, které je zajištěno díky tomu, že je knihovna postavena na rozhraní io.Writer, je možné využít v testech atd.:

package main
 
import (
        "fmt"
        "log"
        "strings"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        asString := &strings.Builder{}
 
        table, err := tablewriter.NewCSV(asString, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetRowLine(true)
        table.SetAutoMergeCellsByColumnIndex([]int{1, 2})
        table.Render()
 
        fmt.Println(asString.String())
}

Tento demonstrační příklad nejdříve získá celou textovou podobu tabulky, kterou může dále zpracovat. V tomto jednoduchém případě jí vytiskne na standardní výstup:

+----------+--------+-------------------+---------+---------+
| SEP 2019 | CHANGE |     LANGUAGE      | RATINGS | CHANGEP |
+----------+--------+-------------------+---------+---------+
|        2 | change | C                 |   15.95 |    0.74 |
+----------+        +-------------------+---------+---------+
|        1 |        | Java              |   13.48 |   -3.18 |
+----------+--------+-------------------+---------+---------+
|        3 |        | Python            |   10.47 |    0.59 |
+----------+--------+-------------------+---------+---------+
|        4 |        | C++               |    7.11 |    1.48 |
+----------+--------+-------------------+---------+---------+
|        5 |        | C#                |    4.58 |    1.18 |
+----------+--------+-------------------+---------+---------+
|        6 |        | Visual Basic      |    4.12 |    0.83 |
+----------+--------+-------------------+---------+---------+
|        7 |        | JavaScript        |    2.54 |    0.41 |
+----------+--------+-------------------+---------+---------+
|        9 | change | PHP               |    2.49 |    0.62 |
+----------+        +-------------------+---------+---------+
|       19 |        | R                 |    2.37 |    1.33 |
+----------+        +-------------------+---------+---------+
|        8 |        | SQL               |    1.76 |   -0.19 |
+----------+        +-------------------+---------+---------+
|       14 |        | Go                |    1.46 |    0.24 |
+----------+        +-------------------+---------+---------+
|       16 |        | Swift             |    1.38 |    0.28 |
+----------+        +-------------------+---------+---------+
|       20 |        | Perl              |    1.30 |    0.26 |
+----------+        +-------------------+---------+---------+
|       12 |        | Assembly language |    1.30 |   -0.08 |
+----------+--------+-------------------+---------+---------+
|       15 |        | Ruby              |    1.24 |    0.03 |
+----------+--------+-------------------+---------+---------+

9. Barevný výstup

Poslední vlastností knihovny tablewriter, o níž se v dnešním článku zmíníme, je podpora pro barevný výstup, resp. přesněji řečeno pro obarvení vybraných částí tabulky. V prvním příkladu s barevným výstupem obarvíme jednotlivé nadpisy sloupců – u každého sloupce může být definována barva popředí, barva pozadí a styl vykreslení (typicky tučné písmo). Konkrétní způsob zobrazení je ovšem do značné míry závislý na nastavení terminálu:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetBorder(false)
 
        table.SetHeaderColor(
                tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
                tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
                tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgWhiteColor},
                tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgWhiteColor},
                tablewriter.Colors{tablewriter.Bold, tablewriter.BgWhiteColor})
 
        table.Render()
}

Výsledek:

Obrázek 1: Tabulka s obarvenými nadpisy sloupců.

Ve druhém příkladu obarvíme i vybrané sloupce:

package main
 
import (
        "log"
        "os"
 
        "github.com/olekukonko/tablewriter"
)
 
func main() {
        table, err := tablewriter.NewCSV(os.Stdout, "tiobe.csv", true)
        if err != nil {
                log.Fatal(err)
        }
        table.SetBorder(false)
 
        table.SetHeaderColor(
                tablewriter.Colors{tablewriter.Bold, tablewriter.BgGreenColor},
                tablewriter.Colors{tablewriter.FgHiRedColor, tablewriter.Bold, tablewriter.BgBlackColor},
                tablewriter.Colors{tablewriter.BgRedColor, tablewriter.FgBlackColor},
                tablewriter.Colors{tablewriter.BgCyanColor, tablewriter.FgYellowColor},
                tablewriter.Colors{tablewriter.Bold, tablewriter.BgWhiteColor})
 
        table.SetColumnColor(
                tablewriter.Colors{tablewriter.FgHiBlackColor},
                tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiRedColor},
                tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiGreenColor},
                tablewriter.Colors{tablewriter.FgBlackColor},
                tablewriter.Colors{tablewriter.Bold, tablewriter.BgWhiteColor})
 
        table.Render()
}

Výsledek:

Obrázek 2: Tabulka s obarvenými nadpisy sloupců i jednotlivými sloupci.

Poznámka: dokonce je možné v případě potřeby zajistit obarvení jednotlivých buněk individuálně, což nám umožňuje zvýraznění některých důležitých hodnot.

10. Nepracuje se pouze s tabulkami: pokročilá knihovna go-pretty

Vývojářům, kteří pracují s jazykem Go, je k dispozici ještě jedna velmi užitečná knihovna, kterou je možné použít pro vykreslení obsahu různých datových struktur, samozřejmě včetně tabulek. Tato knihovna se jmenuje go-pretty a dnes si ukážeme, jaké možnosti nám nabízí právě při vykreslování tabulek. Instalaci této knihovny můžeme provést s využitím modulů jazyka Go: postačuje vytvořit nový projekt a upravit jeho projektový soubor do podoby:

module go-pretty-test
 
go 1.14
 
require (
    github.com/jedib0t/go-pretty/v6 v6.1.0
)

Při prvním pokusu o překlad se stáhnou i další potřebné knihovny:

go: downloading github.com/jedib0t/go-pretty v4.3.0+incompatible
go: downloading github.com/jedib0t/go-pretty/v6 v6.1.0
go: extracting github.com/jedib0t/go-pretty/v6 v6.1.0
go: extracting github.com/jedib0t/go-pretty v4.3.0+incompatible

A dojde ke zpětné úpravě projektového souboru:

module go-pretty-test
 
go 1.14
 
require (
    github.com/go-openapi/strfmt v0.20.0 // indirect
    github.com/jedib0t/go-pretty v4.3.0+incompatible
    github.com/jedib0t/go-pretty/v6 v6.1.0
)

Od této chvíle je možné knihovnu importovat i do dalších projektů.

11. Zobrazení jednoduché tabulky knihovnou go-pretty

Podobně, jako tomu bylo u předchozích třech popisovaných knihoven, si nejdříve ukážeme, jak jsou vlastně zobrazeny jednoduché tabulky. Používá se podobný postup, jaký byl využit i v předchozí knihovně:

  1. Je zkonstruován objekt představující tabulku, a to konstruktorem NewWriter
  2. Nastaví se objekt použitý pro vykreslení, v našem případě os.Stdout
  3. Jednotlivé řádky tabulky se připojí metodou AppendRow
  4. Celá tabulka je vykreslena metodou Render

Důležité je, že řádek tabulky je představován datovým typem table.Row, což ale není nic jiného než řez objektů vyhovujících rozhraní interface{}, tedy jinými slovy jakýchkoli objektů:

package main
 
import (
        "os"
 
        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
        }
        t.Render()
}

Výsledkem spuštění předchozího příkladu by měla být tato tabulka:

+---+------------+------------+------------+
| # | FIRST NAME | LAST NAME  | POPULARITY |
+---+------------+------------+------------+
| 0 | Eliška     | Najbrtová  |          4 |
| 1 | Jenny      | Suk        |          3 |
| 2 | Anička     | Šafářová   |          1 |
| 3 | Sváťa      | Pulec      |          3 |
| 4 | Blažej     | Motyčka    |          8 |
| 5 | Eda        | Wasserfall |          3 |
| 6 | Přemysl    | Hájek      |         10 |
+---+------------+------------+------------+

12. Patička tabulky

Do tabulky je možné (což opět není nic unikátního) přidat patičku, a to konkrétně metodou nazvanou AppendFooter. Této metodě se předává hodnota stejného typu, jako metodám AppendHeader a AppendRow – typ table.Row, což je řez hodnotami splňujícími rozhraní interface{}:

package main
 
import (
        "os"
 
        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        totalPopularity := 0
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
                totalPopularity += role.popularity
        }
        t.AppendFooter(table.Row{"", "", "Total", totalPopularity})
        t.Render()
}

Výsledná tabulka bude obsahovat patičku se součtem popularit jednotlivých postav z jedné známé divadelní hry:

+---+------------+------------+------------+
| # | FIRST NAME | LAST NAME  | POPULARITY |
+---+------------+------------+------------+
| 0 | Eliška     | Najbrtová  |          4 |
| 1 | Jenny      | Suk        |          3 |
| 2 | Anička     | Šafářová   |          1 |
| 3 | Sváťa      | Pulec      |          3 |
| 4 | Blažej     | Motyčka    |          8 |
| 5 | Eda        | Wasserfall |          3 |
| 6 | Přemysl    | Hájek      |         10 |
+---+------------+------------+------------+
|   |            | TOTAL      |         32 |
+---+------------+------------+------------+

13. Styl vykreslení tabulek

Jednou z mnoha předností knihovny go-pretty je existence již předpřipravených stylů aplikovaných při vykreslení tabulek. Styly určují například způsob zobrazení okrajů tabulky, barvu sloupců či nadpisů sloupců atd. Nejprve si ukažme způsob aplikace stylu, který ovlivňuje pouze okraje tabulky a nikoli barvu vykreslení. Styl se vybírá metodou nazvanou SetStyle:

package main
 
import (
        "os"
 
        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
        t.SetStyle(table.StyleLight)
 
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        totalPopularity := 0
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
                totalPopularity += role.popularity
        }
        t.AppendFooter(table.Row{"", "", "Total", totalPopularity})
        t.Render()
}

Výsledkem běhu tohoto příkladu by měla být následující tabulka (založená na speciálních Unicode znacích, což vyžaduje použití vhodného fontu):

┌───┬────────────┬────────────┬────────────┐
│ # │ FIRST NAME │ LAST NAME  │ POPULARITY │
├───┼────────────┼────────────┼────────────┤
│ 0 │ Eliška     │ Najbrtová  │          4 │
│ 1 │ Jenny      │ Suk        │          3 │
│ 2 │ Anička     │ Šafářová   │          1 │
│ 3 │ Sváťa      │ Pulec      │          3 │
│ 4 │ Blažej     │ Motyčka    │          8 │
│ 5 │ Eda        │ Wasserfall │          3 │
│ 6 │ Přemysl    │ Hájek      │         10 │
├───┼────────────┼────────────┼────────────┤
│   │            │ TOTAL      │         32 │
└───┴────────────┴────────────┴────────────┘

14. Ostatní styly okrajů tabulek

Kromě stylu zmíněného v předchozí kapitole existují další tři předpřipravené styly okrajů tabulky, resp. okrajů mezi jejími jednotlivými buňkami:

  1. table.StyleBold
  2. table.StyleDouble
  3. table.StyleRounded

Všechny tři zmíněné styly jsou použity v dalším demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:

package main
 
import (
        "os"
 
        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
 
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        totalPopularity := 0
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
                totalPopularity += role.popularity
        }
        t.AppendFooter(table.Row{"", "", "Total", totalPopularity})
 
        t.SetStyle(table.StyleBold)
        t.Render()
 
        t.SetStyle(table.StyleDouble)
        t.Render()
 
        t.SetStyle(table.StyleRounded)
        t.Render()
}

Opět je patrné, že tyto styly jsou založeny na použití specializovaných Unicode znaků a vyžadují tedy vhodný font:

┏━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ # ┃ FIRST NAME ┃ LAST NAME  ┃ POPULARITY ┃
┣━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━┫
┃ 0 ┃ Eliška     ┃ Najbrtová  ┃          4 ┃
┃ 1 ┃ Jenny      ┃ Suk        ┃          3 ┃
┃ 2 ┃ Anička     ┃ Šafářová   ┃          1 ┃
┃ 3 ┃ Sváťa      ┃ Pulec      ┃          3 ┃
┃ 4 ┃ Blažej     ┃ Motyčka    ┃          8 ┃
┃ 5 ┃ Eda        ┃ Wasserfall ┃          3 ┃
┃ 6 ┃ Přemysl    ┃ Hájek      ┃         10 ┃
┣━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━┫
┃   ┃            ┃ TOTAL      ┃         32 ┃
┗━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━┛
 
╔═══╦════════════╦════════════╦════════════╗
║ # ║ FIRST NAME ║ LAST NAME  ║ POPULARITY ║
╠═══╬════════════╬════════════╬════════════╣
║ 0 ║ Eliška     ║ Najbrtová  ║          4 ║
║ 1 ║ Jenny      ║ Suk        ║          3 ║
║ 2 ║ Anička     ║ Šafářová   ║          1 ║
║ 3 ║ Sváťa      ║ Pulec      ║          3 ║
║ 4 ║ Blažej     ║ Motyčka    ║          8 ║
║ 5 ║ Eda        ║ Wasserfall ║          3 ║
║ 6 ║ Přemysl    ║ Hájek      ║         10 ║
╠═══╬════════════╬════════════╬════════════╣
║   ║            ║ TOTAL      ║         32 ║
╚═══╩════════════╩════════════╩════════════╝
 
╭───┬────────────┬────────────┬────────────╮
│ # │ FIRST NAME │ LAST NAME  │ POPULARITY │
├───┼────────────┼────────────┼────────────┤
│ 0 │ Eliška     │ Najbrtová  │          4 │
│ 1 │ Jenny      │ Suk        │          3 │
│ 2 │ Anička     │ Šafářová   │          1 │
│ 3 │ Sváťa      │ Pulec      │          3 │
│ 4 │ Blažej     │ Motyčka    │          8 │
│ 5 │ Eda        │ Wasserfall │          3 │
│ 6 │ Přemysl    │ Hájek      │         10 │
├───┼────────────┼────────────┼────────────┤
│   │            │ TOTAL      │         32 │
╰───┴────────────┴────────────┴────────────╯

15. Styly se změnou barvy tabulky či její části

Mnohem více předpřipravených stylů definuje mj. i barvy jednotlivých řádků tabulky, nadpisů sloupců atd. Jeden z těchto stylů se jmenuje table.StyleColoredBright. Ukažme si nyní způsob jeho použití:

package main
 
import (
        "os"
 
        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
        t.SetStyle(table.StyleColoredBright)
 
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        totalPopularity := 0
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
                totalPopularity += role.popularity
        }
        t.AppendFooter(table.Row{"", "", "Total", totalPopularity})
        t.Render()
}

Výsledkem by měla (resp. mohla být) tato tabulka:

Obrázek 3: Tabulka se stylem StyleColoredBright.

Poznámka: ve skutečnosti se může způsob konkrétního zobrazení odlišovat v závislosti na nastavení terminálu a jeho schopnostech. Minimálně by měl terminál podporovat osm základních barev pro popředí a pozadí, včetně volby tučného písma.

16. Ostatní připravené barevné styly

Standardních barvových stylů (či témat) existuje v knihovně go-pretty větší množství:

  1. table.StyleColoredBright
  2. table.StyleColoredDark
  3. table.StyleColoredBlackOnBlueWhite
  4. table.StyleColoredBlackOnCyanWhite
  5. table.StyleColoredBlackOnGreenWhite
  6. table.StyleColoredBlackOnMagentaWhite
  7. table.StyleColoredBlackOnYellowWhite
  8. table.StyleColoredBlackOnRedWhite
  9. table.StyleColoredBlueWhiteOnBlack
  10. table.StyleColoredCyanWhiteOnBlack
  11. table.StyleColoredGreenWhiteOnBlack
  12. table.StyleColoredMagentaWhiteOnBlack
  13. table.StyleColoredRedWhiteOnBlack
  14. table.StyleColoredYellowWhiteOnBlack

Následující příklad zobrazí tabulky ve všech možných standardních barevných kombinacích:

package main
 
import (
        "fmt"
        "os"

        "github.com/jedib0t/go-pretty/table"
)
 
// Role represents user role in some IS
type Role struct {
        name       string
        surname    string
        popularity int
}
 
func main() {
        roles := []Role{
                Role{"Eliška", "Najbrtová", 4},
                Role{"Jenny", "Suk", 3},
                Role{"Anička", "Šafářová", 1},
                Role{"Sváťa", "Pulec", 3},
                Role{"Blažej", "Motyčka", 8},
                Role{"Eda", "Wasserfall", 3},
                Role{"Přemysl", "Hájek", 10},
        }
 
        t := table.NewWriter()
        t.SetOutputMirror(os.Stdout)
 
        t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Popularity"})
 
        totalPopularity := 0
        for i, role := range roles {
                t.AppendRow(table.Row{i, role.name, role.surname, role.popularity})
                totalPopularity += role.popularity
        }
        t.AppendFooter(table.Row{"", "", "Total", totalPopularity})
 
        styles := []table.Style{
                table.StyleColoredBright,
                table.StyleColoredDark,
                table.StyleColoredBlackOnBlueWhite,
                table.StyleColoredBlackOnCyanWhite,
                table.StyleColoredBlackOnGreenWhite,
                table.StyleColoredBlackOnMagentaWhite,
                table.StyleColoredBlackOnYellowWhite,
                table.StyleColoredBlackOnRedWhite,
                table.StyleColoredBlueWhiteOnBlack,
                table.StyleColoredCyanWhiteOnBlack,
                table.StyleColoredGreenWhiteOnBlack,
                table.StyleColoredMagentaWhiteOnBlack,
                table.StyleColoredRedWhiteOnBlack,
                table.StyleColoredYellowWhiteOnBlack,
        }
 
        for _, style := range styles {
                fmt.Println(style.Name)
                t.SetStyle(style)
                t.Render()
                fmt.Println()
                fmt.Println()
        }
}

Výsledky:

Obrázek 4: Barvová témata tabulek.

Obrázek 5: Barvová témata tabulek.

Obrázek 6: Barvová témata tabulek.

Obrázek 7: Barvová témata tabulek.

17. Obsah následující části seriálu

V navazující části seriálu o programovacím jazyku Go i o knihovnách a nástrojích, které pro tento jazyk existují, dokončíme popis možností nabízených knihovnou go-pretty. Ukážeme si především způsob vykreslení dalších datových struktur podporovaných touto knihovnou.

18. Příloha: často používané formáty tabulek vykreslených neproporcionálním písmem

Tabulky zapisované v čistých textových souborech (plain ASCII, plain text) se v praxi používají poměrně často, a to už minimálně několik desetiletí (ve skutečnosti ovšem mnohem déle, neboť se jednalo o standardní formát tiskových sestav jak na mainframech, tak i na minipočítačích). V této příloze jsou uvedeny tři formáty, které se ve své oblasti staly de-facto standardy a existují nástroje, které takové tabulky dokážou rozpoznat a korektně zpracovat.

První formát tabulek je používán ve slavném režimu Org v textovém editoru Emacs. Ve skutečnosti tento formát podporují i další nástroje:

| Language          |   Sep 2020 |   Sep 2019 | Change   |   Ratings |   Changep |
|-------------------+------------+------------+----------+-----------+-----------|
| C                 |          1 |          2 | change   |     15.95 |      0.74 |
| Java              |          2 |          1 | change   |     13.48 |     -3.18 |
| Python            |          3 |          3 | nan      |     10.47 |      0.59 |
| C++               |          4 |          4 | nan      |      7.11 |      1.48 |
| C#                |          5 |          5 | nan      |      4.58 |      1.18 |
| Visual Basic      |          6 |          6 | nan      |      4.12 |      0.83 |
| JavaScript        |          7 |          7 | nan      |      2.54 |      0.41 |
| PHP               |          8 |          9 | change   |      2.49 |      0.62 |
| R                 |          9 |         19 | change   |      2.37 |      1.33 |
| SQL               |         10 |          8 | change   |      1.76 |     -0.19 |
| Go                |         11 |         14 | change   |      1.46 |      0.24 |
| Swift             |         12 |         16 | change   |      1.38 |      0.28 |
| Perl              |         13 |         20 | change   |      1.3  |      0.26 |
| Assembly language |         14 |         12 | change   |      1.3  |     -0.08 |
| Ruby              |         15 |         15 | nan      |      1.24 |      0.03 |
| MATLAB            |         16 |         18 | change   |      1.1  |      0.04 |
| Groovy            |         17 |         11 | change   |      0.99 |     -0.52 |
| Rust              |         18 |         33 | change   |      0.92 |      0.55 |
| Objective-C       |         19 |         10 | change   |      0.85 |     -0.99 |
| Dart              |         20 |         24 | change   |      0.77 |      0.13 |

Další formát je akceptován Jirou a podobnými nástroji:

bitcoin_skoleni

|| Language          ||   Sep 2020 ||   Sep 2019 || Change   ||   Ratings ||   Changep ||
| C                 |          1 |          2 | change   |     15.95 |      0.74 |
| Java              |          2 |          1 | change   |     13.48 |     -3.18 |
| Python            |          3 |          3 | nan      |     10.47 |      0.59 |
| C++               |          4 |          4 | nan      |      7.11 |      1.48 |
| C#                |          5 |          5 | nan      |      4.58 |      1.18 |
| Visual Basic      |          6 |          6 | nan      |      4.12 |      0.83 |
| JavaScript        |          7 |          7 | nan      |      2.54 |      0.41 |
| PHP               |          8 |          9 | change   |      2.49 |      0.62 |
| R                 |          9 |         19 | change   |      2.37 |      1.33 |
| SQL               |         10 |          8 | change   |      1.76 |     -0.19 |
| Go                |         11 |         14 | change   |      1.46 |      0.24 |
| Swift             |         12 |         16 | change   |      1.38 |      0.28 |
| Perl              |         13 |         20 | change   |      1.3  |      0.26 |
| Assembly language |         14 |         12 | change   |      1.3  |     -0.08 |
| Ruby              |         15 |         15 | nan      |      1.24 |      0.03 |
| MATLAB            |         16 |         18 | change   |      1.1  |      0.04 |
| Groovy            |         17 |         11 | change   |      0.99 |     -0.52 |
| Rust              |         18 |         33 | change   |      0.92 |      0.55 |
| Objective-C       |         19 |         10 | change   |      0.85 |     -0.99 |
| Dart              |         20 |         24 | change   |      0.77 |      0.13 |

A konečně formát třetí je použít v Restructured Textu (rst):

=================  ==========  ==========  ========  =========  =========
Language             Sep 2020    Sep 2019  Change      Ratings    Changep
=================  ==========  ==========  ========  =========  =========
C                           1           2  change        15.95       0.74
Java                        2           1  change        13.48      -3.18
Python                      3           3  nan           10.47       0.59
C++                         4           4  nan            7.11       1.48
C#                          5           5  nan            4.58       1.18
Visual Basic                6           6  nan            4.12       0.83
JavaScript                  7           7  nan            2.54       0.41
PHP                         8           9  change         2.49       0.62
R                           9          19  change         2.37       1.33
SQL                        10           8  change         1.76      -0.19
Go                         11          14  change         1.46       0.24
Swift                      12          16  change         1.38       0.28
Perl                       13          20  change         1.3        0.26
Assembly language          14          12  change         1.3       -0.08
Ruby                       15          15  nan            1.24       0.03
MATLAB                     16          18  change         1.1        0.04
Groovy                     17          11  change         0.99      -0.52
Rust                       18          33  change         0.92       0.55
Objective-C                19          10  change         0.85      -0.99
Dart                       20          24  change         0.77       0.13
=================  ==========  ==========  ========  =========  =========

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně stovku kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad Stručný popis Cesta
1 01-tablewriter-basic-usage.go jednoduchá tabulka vykreslená balíčkem tablewriter https://github.com/tisnik/go-root/blob/master/article71/01-tablewriter-basic-usage.go
2 02-tablewriter-from-csv.go načtení tabulky uložené do CSV bez hlavičky https://github.com/tisnik/go-root/blob/master/article71/02-tablewriter-from-csv.go
3 03-tablewriter-from-csv-header.go načtení tabulky uložené do CSV s hlavičkou https://github.com/tisnik/go-root/blob/master/article71/03-tablewriter-from-csv-header.go
4 04-tablewriter-left-align.go zarovnání všech hodnot v tabulce doleva https://github.com/tisnik/go-root/blob/master/article71/04-tablewriter-left-align.go
5 05-tablewriter-markdown-format.go tabulka kompatibilní s některými nástroji pro Markdown https://github.com/tisnik/go-root/blob/master/article71/05-tablewriter-markdown-format.go
6 06-tablewriter-set-row-line.go horizontální rozdělení jednotlivých řádků tabulky https://github.com/tisnik/go-root/blob/master/article71/06-tablewriter-set-row-line.go
7 07-tablewriter-merge-cells.go automatické spojení buněk se shodným obsahem https://github.com/tisnik/go-root/blob/master/article71/07-tablewriter-merge-cells.go
8 08-tablewriter-merge-columns.go specifikace sloupců, ve kterých má ke spojení buněk dojít https://github.com/tisnik/go-root/blob/master/article71/08-tablewriter-merge-columns.go
9 09-tablewriter-to-string.go získání řetězce reprezentujícího celou vykreslenou tabulku https://github.com/tisnik/go-root/blob/master/article71/09-tablewriter-to-string.go
10 10-tablewriter-colors.go barevný výstup https://github.com/tisnik/go-root/blob/master/article71/10-tablewriter-colors.go
11 11-tablewriter-colors.go barevný výstup https://github.com/tisnik/go-root/blob/master/article71/11-tablewriter-colors.go
       
12 12-go-pretty-simple-table.go jednoduchá tabulka vykreslená balíčkem go-pretty https://github.com/tisnik/go-root/blob/master/article71/12-go-pretty-simple-table.go
13 13-go-pretty-footer.go nastavení patičky tabulky https://github.com/tisnik/go-root/blob/master/article71/13-go-pretty-footer.go
14 14-go-pretty-style.go styl vykreslení tabulky https://github.com/tisnik/go-root/blob/master/article71/14-go-pretty-style.go
15 15-go-pretty-another-styles.go další podporované styly tabulek https://github.com/tisnik/go-root/blob/master/article71/15-go-pretty-another-styles.go
16 16-go-pretty-color-style.go styl (s barvami) vykreslení tabulky https://github.com/tisnik/go-root/blob/master/article71/16-go-pretty-color-style.go
17 17-go-pretty-other-color-styles.go další podporované barvové styly tabulek https://github.com/tisnik/go-root/blob/master/article71/17-go-pretty-other-color-styles.go

20. Odkazy na Internetu

  1. Standardní balíček text/tabwriter
    https://golang.org/pkg/tex­t/tabwriter/
  2. Elastic tabstops: A better way to indent and align code
    https://nickgravgaard.com/elastic-tabstops/
  3. ASCII Table Writer
    https://github.com/olekukon­ko/tablewriter
  4. TablePrinter
    https://github.com/lensesi­o/tableprinter
  5. go-pretty
    https://github.com/jedib0t/go-pretty
  6. What are the drawbacks of elastic tabstops?
    https://softwareengineerin­g.stackexchange.com/questi­ons/137290/what-are-the-drawbacks-of-elastic-tabstops
  7. Elastic tabstop editors and plugins
    https://stackoverflow.com/qu­estions/28652/elastic-tabstop-editors-and-plugins
  8. Příkaz gofmt
    https://golang.org/cmd/gofmt/
  9. Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
    https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/
  10. Standardní balíček fmt
    https://golang.org/pkg/fmt/
  11. Standardní balíček text/template
    https://golang.org/pkg/text/template/
  12. Pretty print struct variables in Go (Golang)
    https://golangbyexample.com/print-struct-variables-golang/
  13. Golang String Padding Example
    https://golang.cafe/blog/golang-string-padding-example.html
  14. Ultimate Golang String Formatting Cheat Sheet
    https://medium.com/swlh/ultimate-golang-string-formatting-cheat-sheet-234ec92c97da
  15. Box Drawing (znaky Unicode)
    https://en.wikipedia.org/wi­ki/List_of_Unicode_charac­ters#Box_Drawing
  16. Box Drawing (Unicode block)
    https://en.wikipedia.org/wi­ki/Box_Drawing_(Unicode_bloc­k)
  17. Tables in Restructured Text
    https://www.sphinx-doc.org/en/master/usage/res­tructuredtext/basics.html#ta­bles
  18. JIRA Tables
    https://www.idalko.com/jira-tables/
  19. Základy použití režimu org-mode v Emacsu
    https://www.root.cz/clanky/zaklady-pouziti-rezimu-org-mode-v-emacsu/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.