Hlavní navigace

Tvorba sešitů pro tabulkové procesory v programovacím jazyku Go

25. 8. 2020
Doba čtení: 51 minut

Sdílet

V poměrně velkém množství informačních systémů se setkáme s požadavkem na vytváření a export tabulek, které bude možné otevřít v tabulkovém procesoru (spreadsheetu). Pro jazyk Go vzniklo několik knihoven, které export do tabulkových procesorů umožňují. Dnes si stručně představíme dvě z nich.

Obsah

1. Tvorba sešitů pro tabulkové procesory v programovacím jazyku Go

2. Tvorba tabulek ve formátu CSV

3. Vytvoření tabulky s vypočtenými hodnotami

4. Postupný výpočet hodnot do tabulky bez jejich meziuložení

5. Problematické rysy formátu CSV a jejich (ne)řešení balíčkem encoding/csv

6. Vytvoření sešitů ve formátu Office Open XML Workbook (.xlsx)

7. Příklad, který se pokusí vytvořit prázdný sešit

8. Sešit s jedním prázdným listem

9. Sešit s větším množstvím listů

10. Přidání řádků a buněk do sešitu

11. Zjednodušené přidání buňky na řádek s naplněním hodnotou

12. Ukázka podporovaných formátů buněk

13. Podrobnější specifikace formátu zobrazení numerických hodnot

14. Přidání vzorců do sešitu

15. Buňky obsahující hypertextové odkazy

16. Základ práce se styly

17. Sdílení stylů mezi buňkami

18. Obsah navazujících článků

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

20. Odkazy na Internetu

1. Tvorba sešitů pro tabulkové procesory v programovacím jazyku Go

Jedním z relativně častých požadavků kladených na různé informační systémy je požadavek na to, aby bylo možné naměřená či vypočtená data vyexportovat v takovém formátu, který by byl zpracovatelný v tabulkových procesorech (spreadsheet), například v „kancelářských“ aplikacích Gnumeric, Microsoft Excel, LibreOffice Calc či v některých tabulkových procesorech naprogramovaných a dostupných ve formě webových aplikací (Google Doc, Ethercalc). Jedná se o logický požadavek, protože v tabulkových procesorech je možné relativně snadno provádět další analýzy dat, interaktivně vytvářet grafy, provádět různé dotazy nad daty atd. Navíc data vyexportovaná do jednoho z podporovaných a (polo)standardizovaných formátů lze naimportovat například do Jupyter Notebooku, Matlabu atd. a následně je sofistikovaněji zpracovat v těchto prostředích.

Existuje hned několik formátů, které jsou podporovány jak knihovnami určenými pro programovací jazyk Go, tak i tabulkovými procesory. Tyto formáty se od sebe odlišují svými vlastnostmi: některé formáty pouze dokážou ukládat tabulky s hodnotami jen několika datových typů (což způsobuje obecně známé problémy s reprezentací časových údajů či peněžních částek), další formáty již umožňují ukládat vzorce, formátování buněk, styly buněk a některé dokonce i tak složité objekty, jako jsou grafy. Z hlediska kompatibility mezi různými systémy je tak možné se rozhodnout například mezi následujícími formáty:

  • CSV neboli Comma-Separated Values [1] je jedním z nejčastěji používaných souborových formátů v této oblasti, a to přesto, že je export a import CSV v některých případech problematický (například některé české mutace Excelu namísto čárek používají středníky, problémy nastávají s buňkami obsahujícími znaky pro konec řádku atd.). Pokud máte při importu či exportu potíže se zpracováním CSV, můžete v naprosté většině tabulkových procesorů zvolit přesnou specifikaci, jak se má konverze (resp. přesněji řečeno import nebo export) provést. V dnešním článku si ukážeme použití balíčku encoding/csv, který pracuje s CSV podle RFC 4180. Tato de facto norma však zdaleka nepopisuje všechny varianty CSV, s nimiž se můžete v praxi setkat.
  • TSV neboli Tab-Separated Values [2] [3] je velmi podobným formátem, ovšem s tím rozdílem, že oddělovačem jednotlivých buněk je znak tabulátoru. Podobně jako v případě CSV i zde existuje několik voleb, které ovlivňují způsob importu (zda tabulka obsahuje hlavičky sloupců atd.).
  • Existuje i mnoho aplikací, v nichž jsou tabulková data uložena ve formě běžných textových souborů s nějakými oddělovači odlišnými od výše zmíněného tabulátoru (relativně často se jedná o středníky, dvojtečky nebo o znak |). Buď se jedná o zobecnění formátů CSV a TSV [4], nebo může mít textový soubor podobu naformátovaných sloupců s pevnou délkou (a tedy bez problémů čitelných uživatelem). V tomto případě většinou musí tabulkové procesory soubor analyzovat a na základě této analýzy navrhnout, kde se nachází jednotlivé sloupce. Příkladem takového souboru je například https://www.cnb.cz/cs/finan­cni_trhy/devizovy_trh/kur­zy_devizoveho_trhu/denni_kur­z.txt, v němž se jako oddělovače používají právě znaky „|“ a navíc – aby byl import ještě zajímavější – jsou v numerických hodnotách použity čárky namísto teček.
  • Dalším velmi často používaným formátem pro přenos tabulek i celých sešitů (sheet) je formát nazvaný Office Open XML Workbook. Tento formát je součástí skupiny formátů známých též pod zkratkou OOXML (plným jménem Office Open XML) nebo též (z dnes již spíše historických důvodů) Microsoft Open XML. Práci s tímto formátem je věnována druhá část dnešního článku.
  • Podobně koncipovaný je i Open Document Format for Office Applications (ODF), známý též pod kratším jménem OpenDocument. Tímto formátem se dnes ještě nebudeme zabývat, i když je taktéž poměrně často používaný (i když se zdá, že souboj mezi ODF a OOXML je již rozhodnutý).
Poznámka: dnes se budeme zabývat pouze vytvářením souborů s tabulkami či s celými sešity. Druhým problémem – a to vlastně problémem obecně složitějším – je import takových sešitů do datových struktur programovacího jazyka Clojure. Na tuto problematiku se však zaměříme až v navazujících článcích.

2. Tvorba tabulek ve formátu CSV

Nejdříve se budeme zabývat způsobem tvorby tabulek (resp. přesněji řečeno souborů s tabulkami) založenými na formátu CSV neboli Comma-Separated Values. Jedná se o soubory, které obsahují čistá data bez dalších informací o formátování, vzorcích, bez možnosti vložení grafů atd. Pro mnoho účelů – zejména pro přenosy dat mezi heterogenními systémy – může být CSV poměrně dobrou volbou, navíc je jejich tvorba (pokud nebudeme brát v úvahu různé speciální varianty nebo mezní případy) relativně snadná a přímočará. I z tohoto důvodu se již ve standardní knihovně programovacího jazyka Go setkáme s balíčkem encoding/csv, jenž podporuje jak export dat do formátu CSV, tak i jejich import. Více info viz:

$ go doc encoding
 
package encoding // import "encoding"
 
Package encoding defines interfaces shared by other packages that convert
data to and from byte-level and textual representations. Packages that check
for these interfaces include encoding/gob, encoding/json, and encoding/xml.
As a result, implementing an interface once can make a type useful in
multiple encodings. Standard types that implement these interfaces include
time.Time and net.IP. The interfaces come in pairs that produce and consume
encoded data.
 
type BinaryMarshaler interface{ ... }
type BinaryUnmarshaler interface{ ... }
type TextMarshaler interface{ ... }
type TextUnmarshaler interface{ ... }

a:

$ go doc encoding/csv
 
package csv // import "encoding/csv"
 
Package csv reads and writes comma-separated values (CSV) files. There are
many kinds of CSV files; this package supports the format described in RFC
4180.
 
A csv file contains zero or more records of one or more fields per record.
Each record is separated by the newline character. The final record may
optionally be followed by a newline character.
 
    field1,field2,field3
 
White space is considered part of a field.
 
Carriage returns before newline characters are silently removed.
 
Blank lines are ignored. A line with only whitespace characters (excluding
the ending newline character) is not considered a blank line.
 
Fields which start and stop with the quote character " are called
quoted-fields. The beginning and ending quote are not part of the field.
 
The source:
 
    normal string,"quoted-field"
 
results in the fields
 
    {`normal string`, `quoted-field`}
 
Within a quoted-field a quote character followed by a second quote character
is considered a single quote.
 
    "the ""word"" is true","a ""quoted-field"""
 
results in
 
    {`the "word" is true`, `a "quoted-field"`}
 
Newlines and commas may be included in a quoted-field
 
    "Multi-line
    field","comma is ,"
 
results in
 
    {`Multi-line
    field`, `comma is ,`}
 
var ErrTrailingComma = errors.New("extra delimiter at end of line") ...
type ParseError struct{ ... }
type Reader struct{ ... }
    func NewReader(r io.Reader) *Reader
type Writer struct{ ... }
    func NewWriter(w io.Writer) *Writer

Ovšem na druhou stranu má tento balíček taktéž několik omezení (například se orientuje jen na zápis řetězců), takže – což je možná trošku paradoxní – je práce s ním nepatrně komplikovanější než se soubory xlsx, s nimiž se setkáme ve druhé části dnešního článku.

Podívejme se nyní na typický demonstrační příklad, který ukazuje některé základní vlastnosti balíčku encoding/csv. Tento příklad by po svém spuštění měl vytvořit nový soubor nazvaný test1.csv, který bude obsahovat tabulku se třemi sloupci a čtyřmi řádky. Data jsou v tomto případě reprezentována dvourozměrnou datovou strukturou, konkrétně řezem, jehož prvky jsou řezy s řetězci. V příkladu se provede několik operací:

  1. Vytvoří se nový soubor určený pro zápis.
  2. Zkonstruuje se objekt typu csv.Writer určený pro zápis dat ve formátu CSV.
  3. Metodou Writer.Write() se postupně zapíšou jednotlivé řádky tabulky.
  4. Zavolá se metoda Writer.Flush() zajišťující vyprázdnění bufferu.
  5. Metodou Writer.Error() se otestuje, zda při zápisu či operaci flush nedošlo k žádné chybě.

Celý zdrojový kód tohoto demonstračního příkladu vypadá následovně:

package main
 
import (
        "encoding/csv"
        "os"
)
 
// jméno generovaného souboru
const spreadsheetName = "test1.csv"
 
func main() {
        records := [][]string{
                {"first_name", "last_name", "username"},
                {"Rob", "Pike", "rob"},
                {"Ken", "Thompson", "ken"},
                {"Robert", "Griesemer", "gri"},
        }
 
        // vytvoření výstupního souboru s tabulkou
        fout, err := os.Create(spreadsheetName)
        if err != nil {
                panic(err)
        }
 
        // zajištění, že se soubor s tabulkou uzavře při ukončení programu
        defer fout.Close()
 
        // konstrukce objektu pro postupné vytvoření tabulky
        writer := csv.NewWriter(fout)
 
        for _, record := range records {
                // vložení nového řádku (záznamu)
                err := writer.Write(record)
                if err != nil {
                        panic(err)
                }
        }
 
        // pokus o uložení tabulky a vyprázdnění bufferu
        writer.Flush()
 
        // test, zda při Write() nebo Flush() došlo k nějaké chybě
        err = writer.Error()
        if err != nil {
                panic(err)
        }
}

Po spuštění tohoto příkladu by se měl vytvořit soubor s následujícím obsahem:

first_name,last_name,username
Rob,Pike,rob
Ken,Thompson,ken
Robert,Griesemer,gri
Poznámka: povšimněte si, že se v tomto případě jako oddělovače použily skutečně čárky (někdy se ovšem může jednat o středníky) a že řetězcové hodnoty nejsou zapsány v uvozovkách.

Ve skutečnosti je možné příklad zjednodušit, a to tak, že se namísto postupného zápisu jednotlivých řádků tabulky provede zápis celé tabulky. Pro tento účel se používá metoda Write.WriteAll, která navíc provede i operaci Flush. Povšimněte si, že tuto metodu je možné použít pouze v tom případě, že data jsou reprezentována jako řez řezů obsahujících řetězce (ty představují jednotlivé buňky tabulky):

package main
 
import (
        "encoding/csv"
        "os"
)
 
// jméno generovaného souboru
const spreadsheetName = "test2.csv"
 
func main() {
        records := [][]string{
                {"first_name", "last_name", "username"},
                {"Rob", "Pike", "rob"},
                {"Ken", "Thompson", "ken"},
                {"Robert", "Griesemer", "gri"},
        }
 
        // vytvoření výstupního souboru s tabulkou
        fout, err := os.Create(spreadsheetName)
        if err != nil {
                panic(err)
        }
 
        // zajištění, že se soubor s tabulkou uzavře při ukončení programu
        defer fout.Close()
 
        // konstrukce objektu pro postupné vytvoření tabulky
        writer := csv.NewWriter(fout)
 
        // uložení všech řádků (záznamu) a provedení operace Flush
        writer.WriteAll(records)
 
        // test, zda při Write() nebo Flush() došlo k nějaké chybě
        err = writer.Error()
        if err != nil {
                panic(err)
        }
}

Tento příklad, i když je strukturovaný odlišně od příkladu prvního, by měl vytvořit naprosto stejný výstupní soubor:

first_name,last_name,username
Rob,Pike,rob
Ken,Thompson,ken
Robert,Griesemer,gri

3. Vytvoření tabulky s vypočtenými hodnotami

V prvních dvou demonstračních příkladech jsme se soustředili na uložení tabulky, která je v aplikaci reprezentována již předem vytvořenou datovou strukturou kompatibilní s knihovnou encoding/csv, konkrétně řezem obsahujícím další řezy s jednotlivými řetězci. Ovšem mnohdy nemáme data připravena přesně v tomto formátu, někdy existují i situace, v nichž jsou data vytvářena průběžně (logování, sledování síťového provozu atd.). V těchto případech může být výhodnější použít „streaming“, tj. postupné přidávání dat buď do postupně vytvářené datové struktury (v případě programovacího jazyka Go řezu) nebo přímým zápisem dat (po řádcích) do výsledného souboru typu CSV.

Příkladem může být tabulka s vypočtenými faktoriály:

n,n!
0,1
1,1
2,2
3,6
4,24
5,120
6,720
7,5040
8,40320
9,362880
10,3628800
11,39916800
12,479001600
13,6227020800
14,87178291200
15,1307674368000
16,20922789888000
17,355687428096000
18,6402373705728000
19,121645100408832000
20,2432902008176640000

Ve třetím demonstračním příkladu je ukázáno, jakým způsobem je možné postupně vytvářet řez, který reprezentuje obsah tabulky jež má být posléze exportována do formátu CSV:

var records [][]string

Připomeňme si, že řez (slice) představuje v jazyce Go pohled (view) do pole s uloženými daty, přičemž v řezu je uložen offset prvního prvku, délka řezu a jeho kapacita. S využitím funkce append se do řezu přidávají další prvky, což interně znamená, že se pole, do kterého řez představuje pohled, musí v určitých okamžicích realokovat:

// zápis ostatních řádků
for n := int64(0); n <= 20; n++ {
        // výpočet faktoriálu
        f := factorial(n)
 
        // příprava dat pro zápis
        var record []string
        record = append(record, strconv.FormatInt(n, 10))
        record = append(record, strconv.FormatInt(f, 10))
 
        // připojení k výsledkům
        records = append(records, record)
}

V demonstračním příkladu postupně takový řez vytváříme z vypočtených výsledků (funkce faktoriál). Povšimněte si, že je nutné zajistit konverzi dat, protože knihovna encoding/csv podporuje pouze práci s řetězci a nikoli s jinými datovými typy (na rozdíl od knihovny popsané ve druhé části dnešního článku).

Následně je tento řez uložen jedinou operací do souboru typu CSV:

// uložení všech řádků (záznamu) a provedení operace Flush
writer.WriteAll(records)

Úplný zdrojový kód tohoto demonstračního příkladu vypadá takto:

package main
 
import (
        "encoding/csv"
        "os"
        "strconv"
)
 
// jméno generovaného souboru
const spreadsheetName = "test3.csv"
 
func factorial(n int64) int64 {
        switch {
        case n < 0:
                return 1
        case n == 0:
                return 1
        default:
                return n * factorial(n-1)
        }
}
 
func main() {
        // vytvoření výstupního souboru s tabulkou
        fout, err := os.Create(spreadsheetName)
        if err != nil {
                panic(err)
        }
 
        // zajištění, že se soubor s tabulkou uzavře při ukončení programu
        defer fout.Close()
 
        // konstrukce objektu pro postupné vytvoření tabulky
        writer := csv.NewWriter(fout)
 
        // zápis hlavičky
        header := []string{"n", "n!"}
        err = writer.Write(header)
        if err != nil {
                panic(err)
        }
 
        var records [][]string
 
        // zápis ostatních řádků
        for n := int64(0); n <= 20; n++ {
                // výpočet faktoriálu
                f := factorial(n)
 
                // příprava dat pro zápis
                var record []string
                record = append(record, strconv.FormatInt(n, 10))
                record = append(record, strconv.FormatInt(f, 10))
 
                // připojení k výsledkům
                records = append(records, record)
        }
 
        // uložení všech řádků (záznamu) a provedení operace Flush
        writer.WriteAll(records)
 
        // test, zda při Write() nebo Flush() došlo k nějaké chybě
        err = writer.Error()
        if err != nil {
                panic(err)
        }
}

4. Postupný výpočet hodnot do tabulky bez jejich meziuložení

Vzhledem k tomu, že se hodnoty, které se mají do CSV uložit, vytváří postupně řádek po řádku, není nutné mít tyto hodnoty po celou dobu uloženy v operační paměti. Naopak – je možné, aby se každý řádek do CSV uložil samostatně, takže i pro velmi rozsáhlé tabulky nemusí mít generátor CSV velké paměťové nároky.

Poznámka: v minulosti jsme se mnohdy mohli setkat s tím, že některé tabulkové procesory dokázaly zpracovat tabulky omezené na 65535 či 65536 řádků. Toto omezení však dnes již v naprosté většině případů neplatí, takže se mnohdy můžeme setkat se skutečně obrovskými tabulkami.

Podívejme se tedy na to, jakým způsobem je možné tabulku vytvářet postupně, tedy řádek po řádku. Nejprve zapíšeme hlavičku (první řádek tabulky):

// zápis hlavičky
header := []string{"n", "n!"}
err = writer.Write(header)
if err != nil {
        panic(err)
}

Dále postupně vypočteme a ihned zapíšeme jednotlivé hodnoty:

// zápis ostatních řádků
for n := int64(0); n <= 20; n++ {
        // výpočet faktoriálu
        f := factorial(n)
 
        // příprava dat pro zápis
        var record [2]string
        record[0] = strconv.FormatInt(n, 10)
        record[1] = strconv.FormatInt(f, 10)
 
        // zápis řádku
        err = writer.Write(record[:])
        if err != nil {
                panic(err)
        }
}

Poté je možné zápis dokončit, samozřejmě opět s testem na případné chyby:

// pokus o uložení tabulky a vyprázdnění bufferu
writer.Flush()
 
// test, zda při Write() nebo Flush() došlo k nějaké chybě
err = writer.Error()
if err != nil {
        panic(err)
}

Následuje úplný zdrojový kód tohoto demonstračního příkladu:

package main
 
import (
        "encoding/csv"
        "os"
        "strconv"
)
 
// jméno generovaného souboru
const spreadsheetName = "test4.csv"
 
func factorial(n int64) int64 {
        switch {
        case n < 0:
                return 1
        case n == 0:
                return 1
        default:
                return n * factorial(n-1)
        }
}
 
func main() {
        // vytvoření výstupního souboru s tabulkou
        fout, err := os.Create(spreadsheetName)
        if err != nil {
                panic(err)
        }
 
        // zajištění, že se soubor s tabulkou uzavře při ukončení programu
        defer fout.Close()
 
        // konstrukce objektu pro postupné vytvoření tabulky
        writer := csv.NewWriter(fout)
 
        // zápis hlavičky
        header := []string{"n", "n!"}
        err = writer.Write(header)
        if err != nil {
                panic(err)
        }
 
        // zápis ostatních řádků
        for n := int64(0); n <= 20; n++ {
                // výpočet faktoriálu
                f := factorial(n)
 
                // příprava dat pro zápis
                var record [2]string
                record[0] = strconv.FormatInt(n, 10)
                record[1] = strconv.FormatInt(f, 10)
 
                // zápis řádku
                err = writer.Write(record[:])
                if err != nil {
                        panic(err)
                }
        }
 
        // pokus o uložení tabulky a vyprázdnění bufferu
        writer.Flush()
 
        // test, zda při Write() nebo Flush() došlo k nějaké chybě
        err = writer.Error()
        if err != nil {
                panic(err)
        }
}

5. Problematické rysy formátu CSV a jejich (ne)řešení balíčkem encoding/csv

Na tomto místě je nutné poznamenat, že formát souborů CSV nebyl navržen příliš šťastně a navíc nebyl do všech podrobností standardizován. To se v praxi projevuje tím, že se můžeme setkat s CSV, které není možné v tabulkových procesorech otevřít, popř. je sice otevřít možné je, ovšem dojde k poškození dat. Setkat se můžeme s CSV, v němž je oddělovačem středník a nikoli čárka, v nichž je čárka použita v roli desetinné čárky (namísto přece jen používanější tečky), problém mohou způsobovat uvozovky v buňkách, konec řádku v buňce (pokud je vůbec podporován), některé CSV mají unixové konce řádků, jiné DOSové atd. Tvůrci knihovny encoding/csv tento problém vyřešili způsobem typickým pro celý jazyk Go – explicitně prohlásili, že podporují pouze podmnožinu CSV specifikovanou v RFC 4180. Zajímavé tedy bude zjistit, jak se tato knihovna vypořádá s tím, kdy do buněk (co jsou „jen“ řetězce) vložíme některé speciální znaky:

  • Apostrofy
  • Uvozovky
  • Konce řádků
  • Znak tabulátoru
  • Čárky v buňkách

Všechny tyto speciální případy jsou použity v dnešním pátém demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:

package main
 
import (
        "encoding/csv"
        "os"
)
 
// jméno generovaného souboru
const spreadsheetName = "test5.csv"
 
func main() {
        records := [][]string{
                {"first\nname", "last\nname", "username"},
                {"'Rob'", "'Pike'", "rob"},
                {"\"Ken\"", "\"Thompson\"", "ken"},
                {"`Robert`", "`Griesemer`", "gri"},
                {"A B", "C\tD", "\n"},
                {"Foo,Bar", "Baz,,,", ","},
        }
 
        // vytvoření výstupního souboru s tabulkou
        fout, err := os.Create(spreadsheetName)
        if err != nil {
                panic(err)
        }
 
        // zajištění, že se soubor s tabulkou uzavře při ukončení programu
        defer fout.Close()
 
        // konstrukce objektu pro postupné vytvoření tabulky
        writer := csv.NewWriter(fout)
 
        for _, record := range records {
                // vložení nového řádku (záznamu)
                err := writer.Write(record)
                if err != nil {
                        panic(err)
                }
        }
 
        // pokus o uložení tabulky a vyprázdnění bufferu
        writer.Flush()
 
        // test, zda při Write() nebo Flush() došlo k nějaké chybě
        err = writer.Error()
        if err != nil {
                panic(err)
        }
}

Podívejme se nyní na obsah souboru CSV, který vznikne po překladu a spuštění tohoto příkladu:

"first
name","last
name",username
'Rob','Pike',rob
"""Ken""","""Thompson""",ken
`Robert`,`Griesemer`,gri
A B,C   D,"
"
"Foo,Bar","Baz,,,",","

Tento soubor bude některými tabulkovými procesory zpracován nekorektně, což je ostatně patrné i z následujícího screenshotu:

6. Vytvoření sešitů ve formátu Office Open XML Workbook (.xlsx)

Druhým velmi často používaným formátem pro přenos tabulek i celých sešitů (sheet) je formát nazvaný Office Open XML Workbook. Tento formát je součástí skupiny formátů známých též pod zkratkou OOXML (plným jménem Office Open XML) nebo též (z dnes již spíše historických důvodů) Microsoft Open XML. Tento formát byl po určitých peripetiích (které by si vyžádaly samostatný článek, viz například https://en.wikipedia.org/wi­ki/Standardization_of_Offi­ce_Open_XML) standardizován, a to hned několikrát. Zejména se jedná o standard ECMA-376, ovšem důležitější je standard ISO/IEC 29500. Tento formát, který interně obsahuje adresářovou strukturu s několika soubory ve formátu XML, které jsou zazipovány, je podporován většinou důležitých tabulkových procesorů, zejména pak těmito aplikacemi:

# Systém
1 Gnumeric
2 LibreOffice Calc
3 Microsoft Excel
4 WPS Office
5 Corel Wordperfect
6 Google Sheets

Soubory ve formátu Office Open XML Workbook, které mají typicky koncovku „.xlsx“, je možné vytvářet i v aplikacích naprogramovaných v jazyku Go. Lze k tomu použít knihovnu XLSX. Instalace této knihovny je snadná. Postačuje pouze vytvořit nový projekt příkazem go mod init a následně do souboru go.mod dopsat požadavek na knihovnu včetně její verze:

module test
 
go 1.13
 
require github.com/tealeg/xlsx/v3 v3.2.0

Následně je možné provést import této knihovny do projektu vytvářeného v Go a knihovnu začít používat:

import (
        ...
        ...
        ...
        "github.com/tealeg/xlsx/v3"
        ...
        ...
        ...
)

Lze si nechat zobrazit i nápovědu k právě nainstalované knihovně:

$ go doc xlsx
 
 
package xlsx // import "github.com/tealeg/xlsx"
 
const DataValidationTypeCustom ...
const DataValidationOperatorBetween ...
const MJD_0 float64 = 2400000.5 ...
const TRUE = 0x01 ...
const RichTextFontFamilyUnspecified RichTextFontFamily = -1 ...
const Helvetica = "Helvetica" ...
const RGB_Light_Green = "FFC6EFCE" ...
const ColWidth = 9.5
const Excel2006MaxRowCount = 1048576
const Excel2006MaxRowIndex = Excel2006MaxRowCount - 1
const NoRowLimit int = -1
const Solid_Cell_Fill = "solid"
const TEMPLATE_DOCPROPS_APP = ...
const TEMPLATE_DOCPROPS_CORE = ...
const TEMPLATE_XL_THEME_THEME = ...
const TEMPLATE__RELS_DOT_RELS = ...
var DefaultDateFormat = builtInNumFmt[14] ...
var HSLModel = color.ModelFunc(hslModel)
func ColIndexToLetters(n int) string
func ColLettersToIndex(letters string) int
func FileToSlice(path string, options ...FileOption) ([][][]string, error)
...
...
...
type WorkBookRels map[string]string
type XLSXReaderError struct{ ... }
type XLSXUnmarshaler interface{ ... }
Poznámka: Office Open XML není zdaleka jediným formátem (či množinou formátů) založenou na zazipované adresářové struktuře s XML. Podobný koncept můžeme vidět v konkurenční specifikaci OpenDocument neboli plným jménem Open Document Format for Office Applications. A například soubory používané Gnumericem mají taktéž formát XML, který je gzipovaný.

7. Příklad, který se pokusí vytvořit prázdný sešit

V dalším demonstračním příkladu se pokusíme vytvořit prázdný sešit. To ve skutečnosti není možné, protože prakticky všechny tabulkové procesory vyžadují, aby sešit obsahoval alespoň jeden (i když zcela prázdný) list. Z tohoto důvodu následující zdrojový kód po svém spuštění vyhodí výjimku, ovšem tento příklad byl vytvořen z toho prostého důvodu, abychom viděli základní rozdíly mezi výše zmíněnou knihovnou encoding/csv a xlsx. Nejdříve se vytvoří nový sešit konstruktorem NewFile:

// konstrukce struktury typu File
worksheet := xlsx.NewFile()

Tento sešit lze dále upravovat a na konci uložit metodou Save, samozřejmě s kontrolou, zda v průběhu ukládání nedošlo k nějaké chybě:

err := worksheet.Save(spreadsheetName)
if err != nil {
        panic(err)
}

Úplný zdrojový kód tohoto (nefunkčního) demonstračního příkladu vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test01.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // pokus o uložení sešitu
        err := worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

Po jeho spuštění se nejdříve vypíše interní struktura uložená do proměnné worksheet a poté dojde (při ukládání) k výše zmíněné výjimce:

&{map[] map[] <nil> false <nil> [] map[] <nil> [] 0x5e9f60 -1}
panic: File.Save(test01.xlsx): File.Write: MarshallParts: MarshalParts: Workbook must contain at least one worksheet
 
goroutine 1 [running]:
main.main()
        /home/ptisnovs/src/go-root/article_67/spreadsheet01.go:21 +0xe1
exit status 2

8. Sešit s jedním prázdným listem

Již v předchozí kapitole jsme viděli, že sešit by měl obsahovat alespoň jeden list (i když ten může být prázdný). K přidání nového listu slouží metoda AddSheet, které se předá jméno listu. Existuje několik podmínek kladených na způsob pojmenování listu; z tohoto důvodu je důležité zkontrolovat i chybovou hodnotu, která se může z této metody vracet. Další demonstrační příklad po svém spuštění vytvoří plnohodnotný sešit, který lze otevřít v prakticky každém moderním tabulkovém procesoru:

Zdrojový kód příkladu je následující:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test02.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
 
        fmt.Println(sheet)
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

9. Sešit s větším množstvím listů

Listů je možné v sešitu mít větší množství (například v Excelu je jejich maximální počet omezen jen dostupnou pamětí, nikoli nějakou umělou konstantou), takže si ukažme další jednoduchý demonstrační příklad, v němž jsou v sešitu vytvořeny tři listy pojmenované jednoduše „Tabulka1“, „Tabulka2“ a „Tabulka3“ Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test03.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání tří listů do sešitu
        names := []string{"Tabulka1", "Tabulka2", "Tabulka3"}
        for _, name := range names {
                // pokus o přidání nového listu
                sheet, err := worksheet.AddSheet(name)
                if err != nil {
                        panic(err)
                }
 
                fmt.Println(sheet)
        }
 
        // pokus o uložení sešitu
        err := worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

Výsledek:

10. Přidání řádků a buněk do sešitu

Sešity jsou v knihovně xlsx organizovány hierarchicky:

  1. sešit
  2. list
  3. řádek
  4. buňka (na řádku)

To se projevuje i při přidávání řádků a buněk do sešitu. Přidání nového sešitu již známe:

sheet, err := worksheet.AddSheet("Tabulka1")
if err != nil {
        panic(err)
}
defer sheet.Close()

Do sešitu (struktury uložené do proměnné sheet) přidáme řádek následujícím způsobem – metodou AddRow:

// přidání řádku do tabulky
row := sheet.AddRow()

Buňky se přidávají na konkrétní řádek listu, a to metodou AddCell:

// přidání buňky na řádek
cell := row.AddCell()

Jakmile máme k dispozici strukturu reprezentující buňku, můžeme změnit její obsah:

// naplnění buňky hodnotou
cell.SetString("Hello")

S výsledkem:

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test04.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek
        cell := row.AddCell()
 
        // naplnění buňky hodnotou
        cell.SetString("Hello")
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

11. Zjednodušené přidání buňky na řádek s naplněním hodnotou

Předchozí demonstrační příklad přidával buňku relativně složitým způsobem – na několika programových řádcích. Tento kód je ovšem možné zjednodušit:

// přidání buňky na řádek a naplnění hodnotou
row.AddCell().SetString("Hello")

Se stejným výsledkem, jako v příkladu předchozím:

Opět si pro úplnost ukažme celý zdrojový kód takto upraveného demonstračního příkladu:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test05.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek a naplnění hodnotou
        row.AddCell().SetString("Hello")
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

12. Ukázka podporovaných formátů buněk

Buňky v sešitu uloženém ve formátu xlsx mohou obsahovat hodnoty různých typů, například řetězce, celá čísla, číslo s řádovou čárkou (naschvál nepíšu, že s plovoucí čárkou), pravdivostní hodnoty, datum, čas (velmi důležité typy), nebo může být buňka prázdná. Jednotlivé typy hodnot můžeme do tabulky vložit snadno, protože pro každý typ existuje zvláštní „setter“ pojmenovaný podle daného typu:

row.AddCell().SetString("Hello")
row.AddCell().SetInt(42)
row.AddCell().SetInt64(1000 * 1000 * 1000 * 1000)
row.AddCell().SetFloat(1 / 3.0)
row.AddCell().SetBool(true)
row.AddCell().SetBool(false)
row.AddCell().SetDate(time.Now())
row.AddCell().SetDateTime(time.Now())

Výsledný sešit bude vypadat následovně:

Následuje výpis zdrojového kódu příkladu, který tento sešit vytvořil:

package main
 
import (
        "fmt"
        "time"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test06.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek a naplnění hodnotou
        row.AddCell().SetString("Hello")
        row.AddCell().SetInt(42)
        row.AddCell().SetInt64(1000 * 1000 * 1000 * 1000)
        row.AddCell().SetFloat(1 / 3.0)
        row.AddCell().SetBool(true)
        row.AddCell().SetBool(false)
        row.AddCell().SetDate(time.Now())
        row.AddCell().SetDateTime(time.Now())
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

13. Podrobnější specifikace formátu zobrazení numerických hodnot

Numerické hodnoty mohou být v tabulce zobrazeny různým způsobem. K tomu slouží specifikátory formátu, které naznačují, jak se hodnota zobrazí, kolik bude mít desetinných míst, zda nebudou záporné hodnoty zobrazeny červenou barvou atd. V dalším příkladu je ukázáno, co se stane ve chvíli, kdy stejnou hodnotu (1/3) zobrazíme pokaždé jiným způsobem:

row.AddCell().SetFloatWithFormat(1/3.0, "#0")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.0")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.00")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.000")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.0000")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.00000")
row.AddCell().SetFloatWithFormat(1/3.0, "#0.000000")

S výsledky:

Podrobnosti si vysvětlíme příště, ale již nyní si můžeme ukázat úplný demonstrační příklad:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test07.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek a naplnění hodnotou
        row.AddCell().SetFloatWithFormat(1/3.0, "#0")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.0")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.00")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.000")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.0000")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.00000")
        row.AddCell().SetFloatWithFormat(1/3.0, "#0.000000")
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

14. Přidání vzorců do sešitu

Zvláštním typem buňky je „formula“ obsahující vzorec, který se může vypočítat. Tento vzorec se ovšem zadává bez znaku „rovná se“ na začátku, tedy následovně:

row.AddCell().SetFormula("A1+B1")
row.AddCell().SetFormula("100*C1")

Sešit se vzorci:

Opět si ukažme zdrojový kód příkladu:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test08.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek a naplnění hodnotou
        row.AddCell().SetInt(10)
        row.AddCell().SetInt(20)
        row.AddCell().SetFormula("A1+B1")
        row.AddCell().SetFormula("100*C1")
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

15. Buňky obsahující hypertextové odkazy

Buňky v sešitu mohou obsahovat i hypertextové odkazy, které je možné považovat za speciální typ hodnot. Hypertextový odkaz se programově vytváří metodou SetHyperlink, které se předává trojice parametrů: samotný odkaz (URL), text zobrazený v buňce a taktéž obsah bublinové nápovědy, která se zobrazí ve chvíli, kdy uživatel přejede kurzorem myši nad buňkou s hypertextovým odkazem. Příklad použití:

// přidání buňky na řádek a naplnění hodnotou
row.AddCell().SetHyperlink("https://www.root.cz", "Link na Root", " Informace nejen ze světa Linuxu. ISSN 1212-8309")

S výsledkem:

Opět si pochopitelně ukážeme úplný zdrojový kód tohoto demonstračního příkladu, který vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test09.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        // přidání buňky na řádek a naplnění hodnotou
        row.AddCell().SetHyperlink("https://www.root.cz", "Link na Root", " Informace nejen ze světa Linuxu. ISSN 1212-8309")
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

16. Základ práce se styly

V této kapitole si – prozatím ovšem ve stručnosti – ukážeme základy práce se styly buněk. Formát .xlsx se totiž od CSV odlišuje i v tom, že buňkám, z nichž se jednotlivé listy skládají, je možné přiřadit styl. Ten je reprezentován datovou strukturou, která vznikne konstruktorem:

style := xlsx.NewStyle()

Styly buněk jsou rozděleny podle své funkce do několika skupin: zarovnání, barva pozadí a popředí, font atd. Dnes se styly nebudeme zabývat do všech podrobností, takže si jen ukážeme přiřazení nového stylu buňce:

style.Alignment.Horizontal = "right"
style.Fill.FgColor = "FFa0FFa0"
style.Fill.PatternType = "solid"
style.Font.Bold = true
style.ApplyAlignment = true
style.ApplyFill = true
style.ApplyFont = true

Takto vytvořený styl přiřadíme buňce či několika buňkám:

cell := row.AddCell()
 
// nastavení stylu buňky
cell.SetStyle(style)

S výsledkem:

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test10.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        style := xlsx.NewStyle()
        style.Alignment.Horizontal = "right"
        style.Fill.FgColor = "FFa0FFa0"
        style.Fill.PatternType = "solid"
        style.Font.Bold = true
        style.ApplyAlignment = true
        style.ApplyFill = true
        style.ApplyFont = true
 
        // přidání buňky na řádek a naplnění hodnotou
        cell := row.AddCell()
        cell.SetString("Test")
 
        // nastavení stylu buňky
        cell.SetStyle(style)
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

17. Sdílení stylů mezi buňkami

V dnešním posledním demonstračním příkladu si ukážeme, co se stane ve chvíli, kdy styl přiřadíme dvěma buňkám, ovšem mezi přiřazením daný styl změníme. Nejprve se styl vytvoří a přiřadí první buňce, což pro nás není nic nového:

// přidání první buňky na řádek a naplnění hodnotou
cell := row.AddCell()
cell.SetString("Test #1")
 
// nastavení stylu buňky
cell.SetStyle(style)

Následně styl změníme, ovšem již bez jakýchkoli pokusů o modifikaci předchozí buňky:

style.Fill.FgColor = "ffffa0a0"
style.Fill.PatternType = "solid"
style.Font.Bold = false

A nakonec vytvoříme druhou buňku a přiřadíme jí upravený styl:

// přidání druhé buňky na řádek a naplnění hodnotou
cell = row.AddCell()
cell.SetString("Test #1")
 
// nastavení stylu buňky
cell.SetStyle(style)

Výsledek možná nemusí plně odpovídat našemu očekávání, protože styl bude u obou buněk totožný. Ve skutečnosti totiž buňky obsahují referenci na styl, takže pokud se styl změní, změní se automaticky i styl všech odpovídajících buněk:

Úplný zdrojový kód dnešního posledního demonstračního příkladu vypadá následovně:

package main
 
import (
        "fmt"
 
        "github.com/tealeg/xlsx/v3"
)
 
// jméno generovaného souboru
const spreadsheetName = "test11.xlsx"
 
func main() {
        // konstrukce struktury typu File
        worksheet := xlsx.NewFile()
 
        fmt.Println(worksheet)
 
        // přidání listu do sešitu
        sheet, err := worksheet.AddSheet("Tabulka1")
        if err != nil {
                panic(err)
        }
        defer sheet.Close()
 
        fmt.Println(sheet)
 
        // přidání řádku do tabulky
        row := sheet.AddRow()
 
        style := xlsx.NewStyle()
        style.Alignment.Horizontal = "right"
        style.Fill.FgColor = "ffa0ffa0"
        style.Fill.PatternType = "solid"
        style.Font.Bold = true
        style.ApplyAlignment = true
        style.ApplyFill = true
        style.ApplyFont = true
 
        // přidání první buňky na řádek a naplnění hodnotou
        cell := row.AddCell()
        cell.SetString("Test #1")
 
        // nastavení stylu buňky
        cell.SetStyle(style)
 
        style.Fill.FgColor = "ffffa0a0"
        style.Fill.PatternType = "solid"
        style.Font.Bold = false
 
        // přidání druhé buňky na řádek a naplnění hodnotou
        cell = row.AddCell()
        cell.SetString("Test #1")
 
        // nastavení stylu buňky
        cell.SetStyle(style)
 
        // pokus o uložení sešitu
        err = worksheet.Save(spreadsheetName)
        if err != nil {
                panic(err)
        }
}

18. Obsah navazujících článků

V dnešním článku jsme si popsali pouze některé možnosti nabízené formátem Office Open XML Workbook a tím pádem i knihovnou xlsx. Příště si ukážeme především podrobnější práci se styly, sdílení stylů mezi jednotlivými listy, vkládání složitějších typů objektů atd.

Root tip

Existuje ještě jeden zajímavý způsob, jak uživatelům zpřístupnit tabulková data, a to dokonce bez nutnosti jejich manuálního importu do tabulkového procesoru. Je totiž možné použít aplikační programové rozhraní tabulkových procesorů dostupných online, tedy ve formě webových aplikací. Mezi známé tabulkové procesory tohoto typu patří Google Sheets ze sady Google Doc. Přes API je možné tabulky (resp. přesněji řečeno sešity a jejich jednotlivé listy) číst či do nich zapisovat přímo z aplikací nebo služeb. Tomuto zajímavému konceptu bude věnován samostatný článek (zaměříme se přitom na použití programovacího jazyka Go a taktéž na použití Pythonu).

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 csv1.go vytvoření jednoduché tabulky se čtyřmi řádky https://github.com/tisnik/go-root/blob/master/article67/csv1.go
2 csv2.go uložení celého obsahu tabulky jediným příkazem https://github.com/tisnik/go-root/blob/master/article67/csv2.go
3 csv3.go vytvoření tabulky s vypočtenými hodnotami https://github.com/tisnik/go-root/blob/master/article67/csv3.go
4 csv4.go alternativní přístup se „streamováním“ výsledků https://github.com/tisnik/go-root/blob/master/article67/csv4.go
5 csv5.go ukázky omezení formátu CSV https://github.com/tisnik/go-root/blob/master/article67/csv5.go
       
6 spreadsheet01.go příklad, který se pokusí vytvořit prázdný sešit ve formátu Office Open XML Workbook https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet01.go
7 spreadsheet02.go sešit s jedním prázdným listem https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet02.go
8 spreadsheet03.go sešit se třemi prázdnými listy https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet03.go
9 spreadsheet04.go přidání řádků a buněk do sešitu https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet04.go
10 spreadsheet05.go zjednodušené přidání buňky na řádek s naplněním hodnotou https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet05.go
11 spreadsheet06.go ukázka podporovaných formátů buněk https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet06.go
12 spreadsheet07.go podrobnější specifikace formátu zobrazení numerických hodnot https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet07.go
13 spreadsheet08.go přidání vzorců do sešitu https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet08.go
14 spreadsheet09.go buňky obsahující hypertextové odkazy https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet09.go
15 spreadsheet10.go základ práce se styly https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet10.go
16 spreadsheet11.go sdílení stylů mezi buňkami https://github.com/tisnik/go-root/blob/master/article67/spre­adsheet11.go

20. Odkazy na Internetu

  1. Spreadsheet
    https://en.wikipedia.org/wi­ki/Spreadsheet
  2. List of spreadsheet software
    https://en.wikipedia.org/wi­ki/List_of_spreadsheet_sof­tware
  3. Processing spreadsheet data in Go
    https://appliedgo.net/spreadsheet/
  4. Package encoding/csv
    https://golang.org/pkg/en­coding/csv/#example_Writer
  5. Package sheets
    https://godoc.org/google.go­lang.org/api/sheets/v4
  6. Package xlsx
    https://github.com/tealeg/xlsx
  7. Comma-separated values
    https://en.wikipedia.org/wiki/Comma-separated_values
  8. Common Format and MIME Type for Comma-Separated Values (CSV) Files
    https://tools.ietf.org/html/rfc4180
  9. Tab-separated values
    https://en.wikipedia.org/wiki/Tab-separated_values
  10. Office Open XML (Wikipedia)
    https://en.wikipedia.org/wi­ki/Office_Open_XML
  11. Standard ECMA-376: Office Open XML File Formats
    https://www.ecma-international.org/publica­tions/standards/Ecma-376.htm
  12. Adventure for the Atari 2600 Video Game Console by Warren Robinett
    http://www.warrenrobinett­.com/adventure/index.html
  13. Mapa hry Adventure
    http://www.warrenrobinett­.com/adventure/adv-map1.gif
  14. Integration Basics
    https://gafferongames.com/pos­t/integration_basics/
  15. Fix Your Timestep!
    https://gafferongames.com/pos­t/fix_your_timestep/
  16. Handling variable frame rate in SDL2
    https://gamedev.stackexchan­ge.com/questions/151877/han­dling-variable-frame-rate-in-sdl2
  17. Too Fast! (SDL fórum)
    https://discourse.libsdl.org/t/too-fast/11128
  18. Performance tricks (SDL fórum)
    https://discourse.libsdl.or­g/t/performance-tricks/6685
  19. Achieving a constant frame rate in SDL
    https://stackoverflow.com/qu­estions/2548541/achieving-a-constant-frame-rate-in-sdl
  20. Object’s speed control against different framerates
    https://discourse.libsdl.org/t/objects-speed-control-against-different-framerates/14497/1
  21. Stránky projektu SDL
    http://www.libsdl.org/
  22. Simple DirectMedia Layer (Wikipedia)
    https://en.wikipedia.org/wi­ki/Simple_DirectMedia_Lay­er
  23. SDL Language Bindings
    http://www.libsdl.org/languages.php
  24. SDL version 1.2.15
    http://www.libsdl.org/download-1.2.php
  25. SDL version 2.0.1
    http://www.libsdl.org/download-2.0.php
  26. Rozhraní go-sdl2
    https://github.com/veandco/go-sdl2
  27. Dokumentace k rozhraní go-sdl2
    https://godoc.org/github.com/ve­andco/go-sdl2
  28. Dokumentace k balíčku sdl
    https://godoc.org/github.com/ve­andco/go-sdl2/sdl
  29. Dokumentace k balíčku gfx
    https://godoc.org/github.com/ve­andco/go-sdl2/gfx
  30. Cross-platform games development (part 1)
    http://renatoc.wait4.org/2010/02/04/cross-platform-games-development-part-1/
  31. Cross-platform games development (part 2)
    http://renatoc.wait4.org/tag/sdljava/
  32. Go Data Structures: Binary Search Tree
    https://flaviocopes.com/golang-data-structure-binary-search-tree/
  33. Gobs of data
    https://blog.golang.org/gobs-of-data
  34. Formát BSON
    http://bsonspec.org/
  35. Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
    https://blog.intelligentbe­e.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/
  36. Tvorba univerzálních projevů
    http://www.kyblsoft.cz/projevy
  37. Repositář projektu Gift
    https://github.com/disinte­gration/gift
  38. Dokumentace k projektu Gift
    https://godoc.org/github.com/di­sintegration/gift
  39. Online x86 / x64 Assembler and Disassembler
    https://defuse.ca/online-x86-assembler.htm#disassembly2
  40. The Design of the Go Assembler
    https://talks.golang.org/2016/as­m.slide#1
  41. A Quick Guide to Go's Assembler
    https://golang.org/doc/asm
  42. AssemblyPolicy
    https://github.com/golang/go/wi­ki/AssemblyPolicy
  43. Geohash in Golang Assembly
    https://mmcloughlin.com/posts/geohash-assembly
  44. Command objdump
    https://golang.org/cmd/objdump/
  45. Assembly
    https://goroutines.com/asm
  46. Go & Assembly
    http://www.doxsey.net/blog/go-and-assembly
  47. A Foray Into Go Assembly Programming
    https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/
  48. Golang Capturing log.Println And fmt.Println Output
    https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4
  49. Stránka projektu plotly
    https://plot.ly/
  50. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  51. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  52. Michael Fogleman's projects
    https://www.michaelfogleman­.com/projects/tagged/grap­hics/
  53. Color Graphs of Complex Functions
    https://web.archive.org/web/20120511021419/htt­p://w.american.edu/cas/mat­hstat/lcrone/ComplexPlot.html
  54. A Gallery of Complex Functions
    http://wismuth.com/complex/ga­llery.html
  55. package glot
    https://godoc.org/github.com/A­rafatk/glot
  56. Gnuplotting: Output terminals
    http://www.gnuplotting.org/output-terminals/
  57. Introducing Glot the plotting library for Golang
    https://medium.com/@Arafat­./introducing-glot-the-plotting-library-for-golang-3133399948a1
  58. Introducing Glot the plotting library for Golang
    https://blog.gopheracademy.com/advent-2018/introducing-glot/
  59. Glot is a plotting library for Golang built on top of gnuplot
    https://github.com/Arafatk/glot
  60. Example plots (gonum/plot)
    https://github.com/gonum/plot/wi­ki/Example-plots
  61. A repository for plotting and visualizing data (gonum/plot)
    https://github.com/gonum/plot
  62. golang library to make https://chartjs.org/ plots
    https://github.com/brentp/go-chartjs
  63. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  64. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  65. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  66. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  67. gonum
    https://github.com/gonum
  68. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  69. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  70. Gonum Numerical Packages
    https://www.gonum.org/
  71. Stránky projektu MinIO
    https://min.io/
  72. MinIO Quickstart Guide
    https://docs.min.io/docs/minio-quickstart-guide.html
  73. MinIO Go Client API Reference
    https://docs.min.io/docs/golang-client-api-reference
  74. MinIO Python Client API Reference
    https://docs.min.io/docs/python-client-api-reference.html
  75. Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
    https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/
  76. Benchmarking MinIO vs. AWS S3 for Apache Spark
    https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/
  77. MinIO Client Quickstart Guide
    https://docs.min.io/docs/minio-client-quickstart-guide.html
  78. Analýza kvality zdrojových kódů Minia
    https://goreportcard.com/re­port/github.com/minio/minio
  79. This is MinIO
    https://www.youtube.com/wat­ch?v=vF0lQh0XOCs
  80. Running MinIO Standalone
    https://www.youtube.com/wat­ch?v=dIQsPCHvHoM
  81. „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
    https://www.youtube.com/wat­ch?v=wlpn8K0jJ4U
  82. Ginkgo
    http://onsi.github.io/ginkgo/
  83. Gomega
    https://onsi.github.io/gomega/
  84. Ginkgo's Preferred Matcher Library na GitHubu
    https://github.com/onsi/gomega/
  85. Provided Matchers
    http://onsi.github.io/gomega/#provided-matchers
  86. Dokumentace k balíčku goexpect
    https://godoc.org/github.com/go­ogle/goexpect
  87. Balíček goexpect
    https://github.com/google/goexpect
  88. Balíček go-expect
    https://github.com/Netflix/go-expect
  89. Balíček gexpect
    https://github.com/Thomas­Rooney/gexpect
  90. Expect (originál naprogramovaný v TCL)
    https://core.tcl-lang.org/expect/index
  91. Expect (Wikipedia)
    https://en.wikipedia.org/wiki/Expect
  92. Pexpect
    https://pexpect.readthedoc­s.io/en/stable/
  93. Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
    http://networkbit.ch/golang-ssh-client/
  94. goblin na GitHubu
    https://github.com/franela/goblin
  95. Mocha framework
    https://mochajs.org/
  96. frisby na GitHubu
    https://github.com/verdverm/frisby
  97. package frisby
    https://godoc.org/github.com/ver­dverm/frisby
  98. Frisby alternatives and similar packages (generováno)
    https://go.libhunt.com/frisby-alternatives
  99. Cucumber for golang
    https://github.com/DATA-DOG/godog
  100. How to Use Godog for Behavior-driven Development in Go
    https://semaphoreci.com/com­munity/tutorials/how-to-use-godog-for-behavior-driven-development-in-go
  101. Comparative Analysis Of GoLang Testing Frameworks
    https://www.slideshare.net/Dushy­antBhalgami/comparative-analysis-of-golang-testing-frameworks
  102. A Quick Guide to Testing in Golang
    https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/
  103. Tom's Obvious, Minimal Language.
    https://github.com/toml-lang/toml
  104. xml.org
    http://www.xml.org/
  105. Soubory .properties
    https://en.wikipedia.org/wi­ki/.properties
  106. Soubory INI
    https://en.wikipedia.org/wi­ki/INI_file
  107. JSON to YAML
    https://www.json2yaml.com/
  108. Data Format Converter
    https://toolkit.site/format.html
  109. Viper na GitHubu
    https://github.com/spf13/viper
  110. GoDotEnv na GitHubu
    https://github.com/joho/godotenv
  111. The fantastic ORM library for Golang
    http://gorm.io/
  112. Dokumentace k balíčku gorilla/mux
    https://godoc.org/github.com/go­rilla/mux
  113. Gorilla web toolkitk
    http://www.gorillatoolkit.org/
  114. Metric types
    https://prometheus.io/doc­s/concepts/metric_types/
  115. Histograms with Prometheus: A Tale of Woe
    http://linuxczar.net/blog/2017/06/15/pro­metheus-histogram-2/
  116. Why are Prometheus histograms cumulative?
    https://www.robustperception.io/why-are-prometheus-histograms-cumulative
  117. Histograms and summaries
    https://prometheus.io/doc­s/practices/histograms/
  118. Instrumenting Golang server in 5 min
    https://medium.com/@gsisi­mogang/instrumenting-golang-server-in-5-min-c1c32489add3
  119. Semantic Import Versioning in Go
    https://www.aaronzhuo.com/semantic-import-versioning-in-go/
  120. Sémantické verzování
    https://semver.org/
  121. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  122. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  123. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  124. Modules
    https://github.com/golang/go/wi­ki/Modules
  125. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  126. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  127. Go Lang: Memory Management and Garbage Collection
    https://vikash1976.wordpres­s.com/2017/03/26/go-lang-memory-management-and-garbage-collection/
  128. Golang Internals, Part 4: Object Files and Function Metadata
    https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html
  129. What is REPL?
    https://pythonprogramminglan­guage.com/repl/
  130. What is a REPL?
    https://codewith.mu/en/tu­torials/1.0/repl
  131. Programming at the REPL: Introduction
    https://clojure.org/guides/re­pl/introduction
  132. What is REPL? (Quora)
    https://www.quora.com/What-is-REPL
  133. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  134. Read-eval-print loop (Wikipedia)
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  135. Vim as a Go (Golang) IDE using LSP and vim-go
    https://octetz.com/posts/vim-as-go-ide
  136. gopls
    https://github.com/golang/go/wi­ki/gopls
  137. IDE Integration Guide
    https://github.com/stamble­rre/gocode/blob/master/doc­s/IDE_integration.md
  138. How to instrument Go code with custom expvar metrics
    https://sysdig.com/blog/golang-expvar-custom-metrics/
  139. Golang expvar metricset (Metricbeat Reference)
    https://www.elastic.co/gu­ide/en/beats/metricbeat/7­.x/metricbeat-metricset-golang-expvar.html
  140. Package expvar
    https://golang.org/pkg/expvar/#NewInt
  141. Java Platform Debugger Architecture: Overview
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jpda/jpda­.html
  142. The JVM Tool Interface (JVM TI): How VM Agents Work
    https://www.oracle.com/technet­work/articles/javase/index-140680.html
  143. JVM Tool Interface Version 11.0
    https://docs.oracle.com/en/ja­va/javase/11/docs/specs/jvmti­.html
  144. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  145. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  146. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  147. Go & cgo: integrating existing C code with Go
    http://akrennmair.github.io/golang-cgo-slides/#1
  148. Using cgo to call C code from within Go code
    https://wenzr.wordpress.com/2018/06/07/u­sing-cgo-to-call-c-code-from-within-go-code/
  149. Package trace
    https://golang.org/pkg/runtime/trace/
  150. Introducing HTTP Tracing
    https://blog.golang.org/http-tracing
  151. Command trace
    https://golang.org/cmd/trace/
  152. A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
    https://github.com/wesovilabs/koazee
  153. Funkce vyššího řádu v knihovně Underscore
    https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/
  154. Delve: a debugger for the Go programming language.
    https://github.com/go-delve/delve
  155. Příkazy debuggeru Delve
    https://github.com/go-delve/delve/tree/master/Do­cumentation/cli
  156. Debuggery a jejich nadstavby v Linuxu
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  157. Debuggery a jejich nadstavby v Linuxu (2. část)
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  158. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  159. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  160. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  161. Debugging Go Code with GDB
    https://golang.org/doc/gdb
  162. Debugging Go (golang) programs with gdb
    https://thornydev.blogspot­.com/2014/01/debugging-go-golang-programs-with-gdb.html
  163. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  164. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  165. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  166. The LLDB Debugger
    http://lldb.llvm.org/
  167. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  168. 13 Linux Debuggers for C++ Reviewed
    http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817
  169. Go is on a Trajectory to Become the Next Enterprise Programming Language
    https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e
  170. Go Proverbs: Simple, Poetic, Pithy
    https://go-proverbs.github.io/
  171. Handling Sparse Files on Linux
    https://www.systutorials.com/136652/han­dling-sparse-files-on-linux/
  172. Gzip (Wikipedia)
    https://en.wikipedia.org/wiki/Gzip
  173. Deflate
    https://en.wikipedia.org/wiki/DEFLATE
  174. 10 tools written in Go that every developer needs to know
    https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/
  175. Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
    https://www.root.cz/clanky/he­xadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/
  176. Hex dump
    https://en.wikipedia.org/wi­ki/Hex_dump
  177. Rozhraní io.ByteReader
    https://golang.org/pkg/io/#ByteReader
  178. Rozhraní io.RuneReader
    https://golang.org/pkg/io/#RuneReader
  179. Rozhraní io.ByteScanner
    https://golang.org/pkg/io/#By­teScanner
  180. Rozhraní io.RuneScanner
    https://golang.org/pkg/io/#Ru­neScanner
  181. Rozhraní io.Closer
    https://golang.org/pkg/io/#Closer
  182. Rozhraní io.Reader
    https://golang.org/pkg/io/#Reader
  183. Rozhraní io.Writer
    https://golang.org/pkg/io/#Writer
  184. Typ Strings.Reader
    https://golang.org/pkg/strin­gs/#Reader
  185. VACUUM (SQL)
    https://www.sqlite.org/lan­g_vacuum.html
  186. VACUUM (Postgres)
    https://www.postgresql.or­g/docs/8.4/sql-vacuum.html
  187. go-cron
    https://github.com/rk/go-cron
  188. gocron
    https://github.com/jasonlvhit/gocron
  189. clockwork
    https://github.com/whiteShtef/cloc­kwork
  190. clockwerk
    https://github.com/onatm/clockwerk
  191. JobRunner
    https://github.com/bamzi/jobrunner
  192. Rethinking Cron
    https://adam.herokuapp.com/pas­t/2010/4/13/rethinking_cron/
  193. In the Beginning was the Command Line
    https://web.archive.org/web/20180218045352/htt­p://www.cryptonomicon.com/be­ginning.html
  194. repl.it (REPL pro různé jazyky)
    https://repl.it/languages
  195. GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
    https://github.com/jroimartin/gocui
  196. Read–eval–print loop
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  197. go-prompt
    https://github.com/c-bata/go-prompt
  198. readline
    https://github.com/chzyer/readline
  199. A pure golang implementation for GNU-Readline kind library
    https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/
  200. go-readline
    https://github.com/fiorix/go-readline
  201. 4 Python libraries for building great command-line user interfaces
    https://opensource.com/article/17/5/4-practical-python-libraries
  202. prompt_toolkit 2.0.3 na PyPi
    https://pypi.org/project/prom­pt_toolkit/
  203. python-prompt-toolkit na GitHubu
    https://github.com/jonathan­slenders/python-prompt-toolkit
  204. The GNU Readline Library
    https://tiswww.case.edu/php/chet/re­adline/rltop.html
  205. GNU Readline (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Readline
  206. readline — GNU readline interface (Python 3.x)
    https://docs.python.org/3/li­brary/readline.html
  207. readline — GNU readline interface (Python 2.x)
    https://docs.python.org/2/li­brary/readline.html
  208. GNU Readline Library – command line editing
    https://tiswww.cwru.edu/php/chet/re­adline/readline.html
  209. gnureadline 6.3.8 na PyPi
    https://pypi.org/project/gnureadline/
  210. Editline Library (libedit)
    http://thrysoee.dk/editline/
  211. Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
    https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/
  212. libedit or editline
    http://www.cs.utah.edu/~bi­gler/code/libedit.html
  213. WinEditLine
    http://mingweditline.sourceforge.net/
  214. rlcompleter — Completion function for GNU readline
    https://docs.python.org/3/li­brary/rlcompleter.html
  215. rlwrap na GitHubu
    https://github.com/hanslub42/rlwrap
  216. rlwrap(1) – Linux man page
    https://linux.die.net/man/1/rlwrap
  217. readline(3) – Linux man page
    https://linux.die.net/man/3/readline
  218. history(3) – Linux man page
    https://linux.die.net/man/3/history
  219. Dokumentace k balíčku oglematchers
    https://godoc.org/github.com/ja­cobsa/oglematchers
  220. Balíček oglematchers
    https://github.com/jacobsa/o­glematchers
  221. Dokumentace k balíčku ogletest
    https://godoc.org/github.com/ja­cobsa/ogletest
  222. Balíček ogletest
    https://github.com/jacobsa/ogletest
  223. Dokumentace k balíčku assert
    https://godoc.org/github.com/stret­chr/testify/assert
  224. Testify – Thou Shalt Write Tests
    https://github.com/stretchr/testify/
  225. package testing
    https://golang.org/pkg/testing/
  226. Golang basics – writing unit tests
    https://blog.alexellis.io/golang-writing-unit-tests/
  227. An Introduction to Programming in Go / Testing
    https://www.golang-book.com/books/intro/12
  228. An Introduction to Testing in Go
    https://tutorialedge.net/golang/intro-testing-in-go/
  229. Advanced Go Testing Tutorial
    https://tutorialedge.net/go­lang/advanced-go-testing-tutorial/
  230. GoConvey
    http://goconvey.co/
  231. Testing Techniques
    https://talks.golang.org/2014/tes­ting.slide
  232. 5 simple tips and tricks for writing unit tests in #golang
    https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742
  233. Afinní transformace
    https://cs.wikibooks.org/wi­ki/Geometrie/Afinn%C3%AD_tran­sformace_sou%C5%99adnic
  234. package gg
    https://godoc.org/github.com/fo­gleman/gg
  235. Generate an animated GIF with Golang
    http://tech.nitoyon.com/en/blog/2016/01/07/­go-animated-gif-gen/
  236. Generate an image programmatically with Golang
    http://tech.nitoyon.com/en/blog/2015/12/31/­go-image-gen/
  237. The Go image package
    https://blog.golang.org/go-image-package
  238. Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
    https://github.com/llgcode/draw2d
  239. Draw a rectangle in Golang?
    https://stackoverflow.com/qu­estions/28992396/draw-a-rectangle-in-golang
  240. YAML
    https://yaml.org/
  241. edn
    https://github.com/edn-format/edn
  242. Smile
    https://github.com/FasterXML/smile-format-specification
  243. Protocol-Buffers
    https://developers.google.com/protocol-buffers/
  244. Marshalling (computer science)
    https://en.wikipedia.org/wi­ki/Marshalling_(computer_sci­ence)
  245. Unmarshalling
    https://en.wikipedia.org/wi­ki/Unmarshalling
  246. Introducing JSON
    http://json.org/
  247. Package json
    https://golang.org/pkg/encoding/json/
  248. The Go Blog: JSON and Go
    https://blog.golang.org/json-and-go
  249. Go by Example: JSON
    https://gobyexample.com/json
  250. Writing Web Applications
    https://golang.org/doc/articles/wiki/
  251. Golang Web Apps
    https://www.reinbach.com/blog/golang-webapps-1/
  252. Build web application with Golang
    https://legacy.gitbook.com/bo­ok/astaxie/build-web-application-with-golang/details
  253. Golang Templates – Golang Web Pages
    https://www.youtube.com/wat­ch?v=TkNIETmF-RU
  254. Simple Golang HTTPS/TLS Examples
    https://github.com/denji/golang-tls
  255. Playing with images in HTTP response in golang
    https://www.sanarias.com/blog/1214Pla­yingwithimagesinHTTPrespon­seingolang
  256. MIME Types List
    https://www.freeformatter.com/mime-types-list.html
  257. Go Mutex Tutorial
    https://tutorialedge.net/golang/go-mutex-tutorial/
  258. Creating A Simple Web Server With Golang
    https://tutorialedge.net/go­lang/creating-simple-web-server-with-golang/
  259. Building a Web Server in Go
    https://thenewstack.io/building-a-web-server-in-go/
  260. How big is the pipe buffer?
    https://unix.stackexchange­.com/questions/11946/how-big-is-the-pipe-buffer
  261. How to turn off buffering of stdout in C
    https://stackoverflow.com/qu­estions/7876660/how-to-turn-off-buffering-of-stdout-in-c
  262. setbuf(3) – Linux man page
    https://linux.die.net/man/3/setbuf
  263. setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
    https://linux.die.net/man/3/setvbuf
  264. Select waits on a group of channels
    https://yourbasic.org/golang/select-explained/
  265. Rob Pike: Simplicity is Complicated (video)
    http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893
  266. Algorithms to Go
    https://yourbasic.org/
  267. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/
  268. Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
    https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/
  269. Go Defer Simplified with Practical Visuals
    https://blog.learngoprogram­ming.com/golang-defer-simplified-77d3b2b817ff
  270. 5 More Gotchas of Defer in Go — Part II
    https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa
  271. The Go Blog: Defer, Panic, and Recover
    https://blog.golang.org/defer-panic-and-recover
  272. The defer keyword in Swift 2: try/finally done right
    https://www.hackingwithswift.com/new-syntax-swift-2-defer
  273. Swift Defer Statement
    https://andybargh.com/swift-defer-statement/
  274. Modulo operation (Wikipedia)
    https://en.wikipedia.org/wi­ki/Modulo_operation
  275. Node.js vs Golang: Battle of the Next-Gen Languages
    https://www.hostingadvice­.com/blog/nodejs-vs-golang/
  276. The Go Programming Language (home page)
    https://golang.org/
  277. GoDoc
    https://godoc.org/
  278. Go (programming language), Wikipedia
    https://en.wikipedia.org/wi­ki/Go_(programming_langua­ge)
  279. Go Books (kniha o jazyku Go)
    https://github.com/dariubs/GoBooks
  280. The Go Programming Language Specification
    https://golang.org/ref/spec
  281. Go: the Good, the Bad and the Ugly
    https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
  282. Package builtin
    https://golang.org/pkg/builtin/
  283. Package fmt
    https://golang.org/pkg/fmt/
  284. The Little Go Book (další kniha)
    https://github.com/dariubs/GoBooks
  285. The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
    https://www.safaribookson­line.com/library/view/the-go-programming/9780134190570/e­book_split010.html
  286. Learning Go
    https://www.miek.nl/go/
  287. Go Bootcamp
    http://www.golangbootcamp.com/
  288. Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
    http://www.informit.com/sto­re/programming-in-go-creating-applications-for-the-21st-9780321774637
  289. Introducing Go (Build Reliable, Scalable Programs)
    http://shop.oreilly.com/pro­duct/0636920046516.do
  290. Learning Go Programming
    https://www.packtpub.com/application-development/learning-go-programming
  291. The Go Blog
    https://blog.golang.org/
  292. Getting to Go: The Journey of Go's Garbage Collector
    https://blog.golang.org/ismmkeynote
  293. Go (programovací jazyk, Wikipedia)
    https://cs.wikipedia.org/wi­ki/Go_(programovac%C3%AD_ja­zyk)
  294. Rychle, rychleji až úplně nejrychleji s jazykem Go
    https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/
  295. Installing Go on the Raspberry Pi
    https://dave.cheney.net/2012/09/25/in­stalling-go-on-the-raspberry-pi
  296. How the Go runtime implements maps efficiently (without generics)
    https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics
  297. Niečo málo o Go – Golang (slovensky)
    http://golangsk.logdown.com/
  298. How Many Go Developers Are There?
    https://research.swtch.com/gop­hercount
  299. Most Popular Technologies (Stack Overflow Survery 2018)
    https://insights.stackover­flow.com/survey/2018/#most-popular-technologies
  300. Most Popular Technologies (Stack Overflow Survery 2017)
    https://insights.stackover­flow.com/survey/2017#techno­logy
  301. JavaScript vs. Golang for IoT: Is Gopher Winning?
    https://www.iotforall.com/javascript-vs-golang-iot/
  302. The Go Programming Language: Release History
    https://golang.org/doc/de­vel/release.html
  303. Go 1.11 Release Notes
    https://golang.org/doc/go1.11
  304. Go 1.10 Release Notes
    https://golang.org/doc/go1.10
  305. Go 1.9 Release Notes (tato verze je stále používána)
    https://golang.org/doc/go1.9
  306. Go 1.8 Release Notes (i tato verze je stále používána)
    https://golang.org/doc/go1.8
  307. Go on Fedora
    https://developer.fedorapro­ject.org/tech/languages/go/go-installation.html
  308. Writing Go programs
    https://developer.fedorapro­ject.org/tech/languages/go/go-programs.html
  309. The GOPATH environment variable
    https://tip.golang.org/doc/co­de.html#GOPATH
  310. Command gofmt
    https://tip.golang.org/cmd/gofmt/
  311. The Go Blog: go fmt your code
    https://blog.golang.org/go-fmt-your-code
  312. C? Go? Cgo!
    https://blog.golang.org/c-go-cgo
  313. 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/
  314. 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
    https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd
  315. Gofmt No Longer Allows Spaces. Tabs Only
    https://news.ycombinator.com/i­tem?id=7914523
  316. Why does Go „go fmt“ uses tabs instead of whitespaces?
    https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces
  317. Interactive: The Top Programming Languages 2018
    https://spectrum.ieee.org/sta­tic/interactive-the-top-programming-languages-2018
  318. Go vs. Python
    https://www.peterbe.com/plog/govspy
  319. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  320. A Tour of Go: Type inference
    https://tour.golang.org/basics/14
  321. Go Slices: usage and internals
    https://blog.golang.org/go-slices-usage-and-internals
  322. Go by Example: Slices
    https://gobyexample.com/slices
  323. What is the point of slice type in Go?
    https://stackoverflow.com/qu­estions/2098874/what-is-the-point-of-slice-type-in-go
  324. The curious case of Golang array and slices
    https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335
  325. Introduction to Slices in Golang
    https://www.callicoder.com/golang-slices/
  326. Golang: Understanding ‚null‘ and nil
    https://newfivefour.com/golang-null-nil.html
  327. What does nil mean in golang?
    https://stackoverflow.com/qu­estions/35983118/what-does-nil-mean-in-golang
  328. nils In Go
    https://go101.org/article/nil.html
  329. Go slices are not dynamic arrays
    https://appliedgo.net/slices/
  330. Go-is-no-good (nelze brát doslova)
    https://github.com/ksimka/go-is-not-good
  331. Rust vs. Go
    https://news.ycombinator.com/i­tem?id=13430108
  332. Seriál Programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  333. Modern garbage collection: A look at the Go GC strategy
    https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e
  334. Go GC: Prioritizing low latency and simplicity
    https://blog.golang.org/go15gc
  335. Is Golang a good language for embedded systems?
    https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems
  336. Running GoLang on an STM32 MCU. A quick tutorial.
    https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial
  337. Go, Robot, Go! Golang Powered Robotics
    https://gobot.io/
  338. Emgo: Bare metal Go (language for programming embedded systems)
    https://github.com/ziutek/emgo
  339. UTF-8 history
    https://www.cl.cam.ac.uk/~mgk25/uc­s/utf-8-history.txt
  340. Less is exponentially more
    https://commandcenter.blog­spot.com/2012/06/less-is-exponentially-more.html
  341. Should I Rust, or Should I Go
    https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9
  342. Setting up and using gccgo
    https://golang.org/doc/install/gccgo
  343. Elastic Tabstops
    http://nickgravgaard.com/elastic-tabstops/
  344. Strings, bytes, runes and characters in Go
    https://blog.golang.org/strings
  345. Datový typ
    https://cs.wikipedia.org/wi­ki/Datov%C3%BD_typ
  346. Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
    https://www.root.cz/clanky/pro­gramovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09
  347. Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
    https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06
  348. Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
    https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05
  349. Printf Format Strings
    https://www.cprogramming.com/tu­torial/printf-format-strings.html
  350. Java: String.format
    https://docs.oracle.com/ja­vase/8/docs/api/java/lang/Strin­g.html#format-java.lang.String-java.lang.Object…-
  351. Java: format string syntax
    https://docs.oracle.com/ja­vase/8/docs/api/java/util/For­matter.html#syntax
  352. Selectors
    https://golang.org/ref/spec#Selectors
  353. Calling Go code from Python code
    http://savorywatt.com/2015/09/18/ca­lling-go-code-from-python-code/
  354. Go Data Structures: Interfaces
    https://research.swtch.com/interfaces
  355. How to use interfaces in Go
    http://jordanorelli.com/pos­t/32665860244/how-to-use-interfaces-in-go
  356. Interfaces in Go (part I)
    https://medium.com/golangspec/in­terfaces-in-go-part-i-4ae53a97479c
  357. Part 21: Goroutines
    https://golangbot.com/goroutines/
  358. Part 22: Channels
    https://golangbot.com/channels/
  359. [Go] Lightweight eventbus with async compatibility for Go
    https://github.com/asaske­vich/EventBus
  360. What about Trait support in Golang?
    https://www.reddit.com/r/go­lang/comments/8mfykl/what_a­bout_trait_support_in_golan­g/
  361. Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
    https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/
  362. Control Flow
    https://en.wikipedia.org/wi­ki/Control_flow
  363. Structured programming
    https://en.wikipedia.org/wi­ki/Structured_programming
  364. Control Structures
    https://www.golang-book.com/books/intro/5
  365. Control structures – Go if else statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-if-else-statement.html
  366. Control structures – Go switch case statement
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-switch-case.html
  367. Control structures – Go for loop, break, continue, range
    http://golangtutorials.blog­spot.com/2011/06/control-structures-go-for-loop-break.html
  368. Goroutine IDs
    https://blog.sgmansfield.com/2015/12/go­routine-ids/
  369. Different ways to pass channels as arguments in function in go (golang)
    https://stackoverflow.com/qu­estions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang
  370. justforfunc #22: using the Go execution tracer
    https://www.youtube.com/wat­ch?v=ySy3sR1LFCQ
  371. Single Function Exit Point
    http://wiki.c2.com/?Single­FunctionExitPoint
  372. Entry point
    https://en.wikipedia.org/wi­ki/Entry_point
  373. Why does Go have a GOTO statement?!
    https://www.reddit.com/r/go­lang/comments/kag5q/why_do­es_go_have_a_goto_statemen­t/
  374. Effective Go
    https://golang.org/doc/ef­fective_go.html
  375. GoClipse: an Eclipse IDE for the Go programming language
    http://goclipse.github.io/
  376. GoClipse Installation
    https://github.com/GoClip­se/goclipse/blob/latest/do­cumentation/Installation.md#in­stallation
  377. The zero value of a slice is not nil
    https://stackoverflow.com/qu­estions/30806931/the-zero-value-of-a-slice-is-not-nil
  378. Go-tcha: When nil != nil
    https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic
  379. Nils in Go
    https://www.doxsey.net/blog/nils-in-go