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
15. Buňky obsahující hypertextové odkazy
17. Sdílení stylů mezi buňkami
19. Repositář s demonstračními příklady
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/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.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ý).
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í:
- Vytvoří se nový soubor určený pro zápis.
- Zkonstruuje se objekt typu csv.Writer určený pro zápis dat ve formátu CSV.
- Metodou Writer.Write() se postupně zapíšou jednotlivé řádky tabulky.
- Zavolá se metoda Writer.Flush() zajišťující vyprázdnění bufferu.
- 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
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.
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/wiki/Standardization_of_Office_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{ ... }
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:
- sešit
- list
- řádek
- 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.
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:
20. Odkazy na Internetu
- Spreadsheet
https://en.wikipedia.org/wiki/Spreadsheet - List of spreadsheet software
https://en.wikipedia.org/wiki/List_of_spreadsheet_software - Processing spreadsheet data in Go
https://appliedgo.net/spreadsheet/ - Package encoding/csv
https://golang.org/pkg/encoding/csv/#example_Writer - Package sheets
https://godoc.org/google.golang.org/api/sheets/v4 - Package xlsx
https://github.com/tealeg/xlsx - Comma-separated values
https://en.wikipedia.org/wiki/Comma-separated_values - Common Format and MIME Type for Comma-Separated Values (CSV) Files
https://tools.ietf.org/html/rfc4180 - Tab-separated values
https://en.wikipedia.org/wiki/Tab-separated_values - Office Open XML (Wikipedia)
https://en.wikipedia.org/wiki/Office_Open_XML - Standard ECMA-376: Office Open XML File Formats
https://www.ecma-international.org/publications/standards/Ecma-376.htm - Adventure for the Atari 2600 Video Game Console by Warren Robinett
http://www.warrenrobinett.com/adventure/index.html - Mapa hry Adventure
http://www.warrenrobinett.com/adventure/adv-map1.gif - Integration Basics
https://gafferongames.com/post/integration_basics/ - Fix Your Timestep!
https://gafferongames.com/post/fix_your_timestep/ - Handling variable frame rate in SDL2
https://gamedev.stackexchange.com/questions/151877/handling-variable-frame-rate-in-sdl2 - Too Fast! (SDL fórum)
https://discourse.libsdl.org/t/too-fast/11128 - Performance tricks (SDL fórum)
https://discourse.libsdl.org/t/performance-tricks/6685 - Achieving a constant frame rate in SDL
https://stackoverflow.com/questions/2548541/achieving-a-constant-frame-rate-in-sdl - Object’s speed control against different framerates
https://discourse.libsdl.org/t/objects-speed-control-against-different-framerates/14497/1 - Stránky projektu SDL
http://www.libsdl.org/ - Simple DirectMedia Layer (Wikipedia)
https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer - SDL Language Bindings
http://www.libsdl.org/languages.php - SDL version 1.2.15
http://www.libsdl.org/download-1.2.php - SDL version 2.0.1
http://www.libsdl.org/download-2.0.php - Rozhraní go-sdl2
https://github.com/veandco/go-sdl2 - Dokumentace k rozhraní go-sdl2
https://godoc.org/github.com/veandco/go-sdl2 - Dokumentace k balíčku sdl
https://godoc.org/github.com/veandco/go-sdl2/sdl - Dokumentace k balíčku gfx
https://godoc.org/github.com/veandco/go-sdl2/gfx - Cross-platform games development (part 1)
http://renatoc.wait4.org/2010/02/04/cross-platform-games-development-part-1/ - Cross-platform games development (part 2)
http://renatoc.wait4.org/tag/sdljava/ - Go Data Structures: Binary Search Tree
https://flaviocopes.com/golang-data-structure-binary-search-tree/ - Gobs of data
https://blog.golang.org/gobs-of-data - Formát BSON
http://bsonspec.org/ - Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
https://blog.intelligentbee.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/ - Tvorba univerzálních projevů
http://www.kyblsoft.cz/projevy - Repositář projektu Gift
https://github.com/disintegration/gift - Dokumentace k projektu Gift
https://godoc.org/github.com/disintegration/gift - Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - 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/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - 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 - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 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/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 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 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - 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/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - 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 - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - 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/ - 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 - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - 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 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - 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/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go