Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Go Programming Cookbook
Go Programming Cookbook

Go Programming Cookbook: Over 85 recipes to build modular, readable, and testable Golang applications across various domains , Second Edition

eBook
S$32.99 S$47.99
Paperback
S$59.99
Subscription
Free Trial

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing
Table of content icon View table of contents Preview book icon Preview Book

Go Programming Cookbook

I/O and Filesystems

Go provides excellent support for both basic and complex I/O. The recipes in this chapter will explore common Go interfaces that are used to deal with I/O and show you how to make use of them. The Go standard library frequently uses these interfaces, and they will be used by recipes throughout the book.

You'll learn how to work with data in memory and in the form of streams. You'll see examples of working with files, directories, and the CSV format. The temporary files recipe looks at a mechanism to work with files without the overhead of dealing with name collision and more. Lastly, we'll explore Go standard templates for both plain text and HTML.

These recipes should lay the foundation for the use of interfaces to represent and modify data, and should help you think about data in an abstract and flexible way.

In this chapter, the following recipes will be covered:

  • Using the common I/O interfaces
  • Using the bytes and strings packages
  • Working with directories and files
  • Working with the CSV format
  • Working with temporary files
  • Working with text/template and html/template

Technical requirements

In order to proceed with all the recipes in this chapter, configure your environment according to these steps:

  1. Download and install Go 1.12.6 or greater on your operating system at https://golang.org/doc/install.
  2. Open a Terminal or console application and create and navigate to a project directory, such as ~/projects/go-programming-cookbook. All code will be run and modified from this directory.
  3. Clone the latest code into~/projects/go-programming-cookbook-original, as shown in the following code. It is recommended that you work from that directory rather than typing the examples manually:
          $ git clone git@github.com:PacktPublishing/Go-Programming-Cookbook-Second-Edition.git go-programming-cookbook-original
        

Using the common I/O interfaces

The Go language provides a number of I/O interfaces that are used throughout the standard library. It is best practice to make use of these interfaces wherever possible rather than passing structures or other types directly. Two powerful interfaces we will explore in this recipe are the io.Reader and io.Writer interfaces. These interfaces are used throughout the standard library, and understanding how to use them will make you a better Go developer.

The Reader and Writer interfaces look like this:

type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

Go also makes it easy to combine interfaces. For example, take a look at the following code:

type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}

type ReadSeeker interface {
Reader
Seeker
}

This recipe will also explore an io function called Pipe(), as shown in the following code:

func Pipe() (*PipeReader, *PipeWriter)

The remainder of this book will make use of these interfaces.

How to do it...

The following steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/interfaces.
  2. Navigate to this directory.
  3. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/interfaces

You should see a file called go.mod that contains the following:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/interfaces    
  1. Copy tests from ~/projects/go-programming-cookbook-original/chapter1/interfaces or use this as an exercise to write some of your own code!
  1. Create a file called interfaces.go with the following contents:
        package interfaces

import (
"fmt"
"io"
"os"
)

// Copy copies data from in to out first directly,
// then using a buffer. It also writes to stdout
func Copy(in io.ReadSeeker, out io.Writer) error {
// we write to out, but also Stdout
w := io.MultiWriter(out, os.Stdout)

// a standard copy, this can be dangerous if there's a
// lot of data in in
if _, err := io.Copy(w, in); err != nil {
return err
}

in.Seek(0, 0)

// buffered write using 64 byte chunks
buf := make([]byte, 64)
if _, err := io.CopyBuffer(w, in, buf); err != nil {
return err
}

// lets print a new line
fmt.Println()

return nil
}
  1. Create a file called pipes.go with the following contents:
        package interfaces

import (
"io"
"os"
)

// PipeExample helps give some more examples of using io
//interfaces
func PipeExample() error {
// the pipe reader and pipe writer implement
// io.Reader and io.Writer
r, w := io.Pipe()

// this needs to be run in a separate go routine
// as it will block waiting for the reader
// close at the end for cleanup
go func() {
// for now we'll write something basic,
// this could also be used to encode json
// base64 encode, etc.
w.Write([]byte("test\n"))
w.Close()
}()

if _, err := io.Copy(os.Stdout, r); err != nil {
return err
}
return nil
}
  1. Create a new directory named example and navigate to it.
  2. Create a main.gofile with the following contents:
        package main

import (
"bytes"
"fmt"

"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/bytestrings"
)

func main() {
in := bytes.NewReader([]byte("example"))
out := &bytes.Buffer{}
fmt.Print("stdout on Copy = ")
if err := interfaces.Copy(in, out); err != nil {
panic(err)
}

fmt.Println("out bytes buffer =", out.String())

fmt.Print("stdout on PipeExample = ")
if err := interfaces.PipeExample(); err != nil {
panic(err)
}
}
  1. Run go run ..
  1. You may also run the following:
          $ go build 
$ ./example

You should see the following output:

          $ go run .
          
stdout on Copy = exampleexample
out bytes buffer = exampleexample
stdout on PipeExample = test
  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

The Copy() function copies bytes between interfaces and treats that data like a stream. Thinking of data as streams has a lot of practical uses, especially when working with network traffic or filesystems. The Copy() function also creates a MultiWriter interface that combines two writer streams and writes to them twice using ReadSeeker. If a Reader interface was used instead, rather than seeing exampleexample, you would only see example despite copying to the MultiWriter interface twice. You can also use a buffered write if your stream is not fitted into the memory.

The PipeReader and PipeWriter structures implement the io.Reader and io.Writer interfaces. They're connected, creating an in-memory pipe. The primary purpose of a pipe is to read from a stream while simultaneously writing from the same stream to a different source. In essence, it combines the two streams into a pipe.

Go interfaces are a clean abstraction to wrap data that performs common operations. This is made apparent when doing I/O operations, and so the io package is a great resource for learning about interface composition. The pipe package is often underused, but provides great flexibility with thread safety when linking input and output streams.

Using the bytes and strings packages

The bytes and strings packages have a number of useful helpers to work with and convert the data from string to byte types, and vice versa. They allow the creation of buffers that work with a number of common I/O interfaces.

How to do it...

The following steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/bytestrings.
  2. Navigate to this directory.
  3. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/bytestrings

You should see a file called go.mod that contains the following content:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/bytestrings    
  1. Copy the tests from~/projects/go-programming-cookbook-original/chapter1/bytestrings or use this as an exercise to write some of your own code!
  2. Create a file calledbuffer.gowith the following contents:
        package bytestrings

import (
"bytes"
"io"
"io/ioutil"
)

// Buffer demonstrates some tricks for initializing bytes
//Buffers
// These buffers implement an io.Reader interface
func Buffer(rawString string) *bytes.Buffer {

// we'll start with a string encoded into raw bytes
rawBytes := []byte(rawString)

// there are a number of ways to create a buffer from
// the raw bytes or from the original string
var b = new(bytes.Buffer)
b.Write(rawBytes)

// alternatively
b = bytes.NewBuffer(rawBytes)

// and avoiding the initial byte array altogether
b = bytes.NewBufferString(rawString)

return b
}

// ToString is an example of taking an io.Reader and consuming
// it all, then returning a string
func toString(r io.Reader) (string, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return "", err
}
return string(b), nil
}
  1. Create a file calledbytes.gowith the following contents:
        package bytestrings

import (
"bufio"
"bytes"
"fmt"
)

// WorkWithBuffer will make use of the buffer created by the
// Buffer function
func WorkWithBuffer() error {
rawString := "it's easy to encode unicode into a byte
array"

b := Buffer(rawString)

// we can quickly convert a buffer back into byes with
// b.Bytes() or a string with b.String()
fmt.Println(b.String())

// because this is an io Reader we can make use of
// generic io reader functions such as
s, err := toString(b)
if err != nil {
return err
}
fmt.Println(s)

// we can also take our bytes and create a bytes reader
// these readers implement io.Reader, io.ReaderAt,
// io.WriterTo, io.Seeker, io.ByteScanner, and
// io.RuneScanner interfaces
reader := bytes.NewReader([]byte(rawString))

// we can also plug it into a scanner that allows
// buffered reading and tokenization
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanWords)

// iterate over all of the scan events
for scanner.Scan() {
fmt.Print(scanner.Text())
}

return nil
}
  1. Create a file called string.go with the following contents:
        package bytestrings

import (
"fmt"
"io"
"os"
"strings"
)

// SearchString shows a number of methods
// for searching a string
func SearchString() {
s := "this is a test"

// returns true because s contains
// the word this
fmt.Println(strings.Contains(s, "this"))

// returns true because s contains the letter a
// would also match if it contained b or c
fmt.Println(strings.ContainsAny(s, "abc"))

// returns true because s starts with this
fmt.Println(strings.HasPrefix(s, "this"))

// returns true because s ends with this
fmt.Println(strings.HasSuffix(s, "test"))
}

// ModifyString modifies a string in a number of ways
func ModifyString() {
s := "simple string"

// prints [simple string]
fmt.Println(strings.Split(s, " "))

// prints "Simple String"
fmt.Println(strings.Title(s))

// prints "simple string"; all trailing and
// leading white space is removed
s = " simple string "
fmt.Println(strings.TrimSpace(s))
}

// StringReader demonstrates how to create
// an io.Reader interface quickly with a string
func StringReader() {
s := "simple string\n"
r := strings.NewReader(s)

// prints s on Stdout
io.Copy(os.Stdout, r)
}
  1. Create a new directory named example and navigate to it.
  2. Create a main.go file with the following contents:
        package main

import "github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/bytestrings"

func main() {
err := bytestrings.WorkWithBuffer()
if err != nil {
panic(err)
}

// each of these print to stdout
bytestrings.SearchString()
bytestrings.ModifyString()
bytestrings.StringReader()
}
  1. Run go run ..
  2. You may also run the following:
          $ go build
$ ./example

You should see the following output:

          $ go run .
          
it's easy to encode unicode into a byte array ??
it's easy to encode unicode into a byte array ??
it'seasytoencodeunicodeintoabytearray??true
true
true
true
[simple string]
Simple String
simple string
simple string
  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

The bytes library provides a number of convenience functions when working with data. A buffer, for example, is far more flexible than an array of bytes when working with stream-processing libraries or methods. Once you've created a buffer, it can be used to satisfy an io.Reader interface so that you can take advantage of ioutil functions to manipulate the data. For streaming applications, you'd probably want to use a buffer and a scanner. The bufio package comes in handy for these cases. Sometimes, using an array or slice is more appropriate for smaller datasets or when you have a lot of memory on your machine.

Go provides a lot of flexibility in converting data between interfaces when using these basic types—it's relatively simple to convert between strings and bytes. When working with strings, thestringspackage provides a number of convenience functions to work with, search, and manipulate strings. In some cases, a good regular expression may be appropriate, but most of the time, thestringsandstrconvpackages are sufficient. Thestringspackage allows you to make a string look like a title, split it into an array, or trim whitespace. It also provides aReaderinterface of its own that can be used instead of thebytespackage reader type.

Working with directories and files

Working with directories and files can be difficult when you switch between platforms (Windows and Linux, for example). Go provides cross-platform support to work with files and directories in the os and ioutils packages. We've already seen examples of ioutils, but now we'll explore how to use them in another way!

How to do it...

The following steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/filedirs.
  2. Navigate to this directory.
  1. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/filedirs

You should see a file called go.mod that contains the following contents:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/filedirs
  1. Copy tests from~/projects/go-programming-cookbook-original/chapter1/filedirs or use this as an exercise to write some of your own code!
  2. Create a file called dirs.go with the following contents:
        package filedirs

import (
"errors"
"io"
"os"
)

// Operate manipulates files and directories
func Operate() error {
// this 0755 is similar to what you'd see with Chown
// on a command line this will create a director
// /tmp/example, you may also use an absolute path
// instead of a relative one
if err := os.Mkdir("example_dir", os.FileMode(0755));
err != nil {
return err
}

// go to the /tmp directory
if err := os.Chdir("example_dir"); err != nil {
return err
}

// f is a generic file object
// it also implements multiple interfaces
// and can be used as a reader or writer
// if the correct bits are set when opening
f, err := os.Create("test.txt")
if err != nil {
return err
}

// we write a known-length value to the file and
// validate that it wrote correctly
value := []byte("hellon")
count, err := f.Write(value)
if err != nil {
return err
}
if count != len(value) {
return errors.New("incorrect length returned
from write")
}

if err := f.Close(); err != nil {
return err
}

// read the file
f, err = os.Open("test.txt")
if err != nil {
return err
}

io.Copy(os.Stdout, f)

if err := f.Close(); err != nil {
return err
}

// go to the /tmp directory
if err := os.Chdir(".."); err != nil {
return err
}

// cleanup, os.RemoveAll can be dangerous if you
// point at the wrong directory, use user input,
// and especially if you run as root
if err := os.RemoveAll("example_dir"); err != nil {
return err
}

return nil
}
  1. Create a file called files.gowith the following contents:
        package filedirs

import (
"bytes"
"io"
"os"
"strings"
)

// Capitalizer opens a file, reads the contents,
// then writes those contents to a second file
func Capitalizer(f1 *os.File, f2 *os.File) error {
if _, err := f1.Seek(0, io.SeekStart); err != nil {
return err
}

var tmp = new(bytes.Buffer)

if _, err := io.Copy(tmp, f1); err != nil {
return err
}

s := strings.ToUpper(tmp.String())

if _, err := io.Copy(f2, strings.NewReader(s)); err !=
nil {
return err
}
return nil
}

// CapitalizerExample creates two files, writes to one
//then calls Capitalizer() on both
func CapitalizerExample() error {
f1, err := os.Create("file1.txt")
if err != nil {
return err
}

if _, err := f1.Write([]byte(`this file contains a
number of words and new lines`)); err != nil {
return err
}

f2, err := os.Create("file2.txt")
if err != nil {
return err
}

if err := Capitalizer(f1, f2); err != nil {
return err
}

if err := os.Remove("file1.txt"); err != nil {
return err
}

if err := os.Remove("file2.txt"); err != nil {
return err
}

return nil
}
  1. Create a new directory named example and navigate to it.
  1. Create a main.go file with the following contents:
        package main

import "github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/filedirs"

func main() {
if err := filedirs.Operate(); err != nil {
panic(err)
}

if err := filedirs.CapitalizerExample(); err != nil {
panic(err)
}
}
  1. Run go run ..
  2. You may also run the following:
          $ go build
$ ./example

You should see the following output:

          $ go run . 
          
hello
  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

If you're familiar with files in Unix, the Go os library should feel very familiar. You can perform basically all common operations—Stat a file to collect attributes, collect a file with different permissions, and create and modify directories and files. In this recipe, we performed a number of manipulations to directories and files and then cleaned up after ourselves.

Working with file objects is very similar to working with in-memory streams. Files also provide a number of convenience functions directly, such as Chown, Stat, and Truncate. The easiest way to get comfortable with files is to make use of them. In all the previous recipes, we have to be careful to clean up after our programs.

Working with files is a very common operation when building backend applications. Files can be used for configuration, secret keys, as temporary storage, and more. Go wraps OS system calls using the os package and allows the same functions to operate regardless of whether you're using Windows or Unix.

Once your file is opened and stored in a File structure, it can easily be passed into a number of interfaces (we discussed these interfaces earlier). All the earlier examples can use os.File structures directly instead of buffers and in-memory data streams in order to operate on data stored on the disk . This may be useful for certain techniques, such as writing all logs to stderr and the file at the same time with a single write call.

Working with the CSV format

CSV is a common format that is used to manipulate data. It's common, for example, to import or export a CSV file into Excel. The Go CSV package operates on data interfaces, and as a result, it's easy to write data to a buffer, stdout, a file, or a socket. The examples in this section will show some common ways to get data into and out of the CSV format.

How to do it...

These steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/csvformat.
  2. Navigate to this directory.
  3. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/csvformat

You should see a file called go.mod that contains the following contents:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/csvformat
  1. Copy the tests from~/projects/go-programming-cookbook-original/chapter1/csvformat or use this as an exercise to write some of your own code!
  2. Create a file called read_csv.gowith the following contents:
        package csvformat

import (
"bytes"
"encoding/csv"
"fmt"
"io"
"strconv"
)

// Movie will hold our parsed CSV
type Movie struct {
Title string
Director string
Year int
}

// ReadCSV gives shows some examples of processing CSV
// that is passed in as an io.Reader
func ReadCSV(b io.Reader) ([]Movie, error) {

r := csv.NewReader(b)

// These are some optional configuration options
r.Comma = ';'
r.Comment = '-'

var movies []Movie

// grab and ignore the header for now
// we may also want to use this for a dictionary key or
// some other form of lookup
_, err := r.Read()
if err != nil && err != io.EOF {
return nil, err
}

// loop until it's all processed
for {
record, err := r.Read()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}

year, err := strconv.ParseInt(record[2], 10,
64)
if err != nil {
return nil, err
}

m := Movie{record[0], record[1], int(year)}
movies = append(movies, m)
}
return movies, nil
}
  1. Add this additional function to read_csv.go, as follows:
        // AddMoviesFromText uses the CSV parser with a string
func AddMoviesFromText() error {
// this is an example of us taking a string, converting
// it into a buffer, and reading it
// with the csv package
in := `
- first our headers
movie title;director;year released

- then some data
Guardians of the Galaxy Vol. 2;James Gunn;2017
Star Wars: Episode VIII;Rian Johnson;2017
`

b := bytes.NewBufferString(in)
m, err := ReadCSV(b)
if err != nil {
return err
}
fmt.Printf("%#vn", m)
return nil
}
  1. Create a file called write_csv.gowith thefollowing contents:
        package csvformat

import (
"bytes"
"encoding/csv"
"io"
"os"
)

// A Book has an Author and Title
type Book struct {
Author string
Title string
}

// Books is a named type for an array of books
type Books []Book

// ToCSV takes a set of Books and writes to an io.Writer
// it returns any errors
func (books *Books) ToCSV(w io.Writer) error {
n := csv.NewWriter(w)
err := n.Write([]string{"Author", "Title"})
if err != nil {
return err
}
for _, book := range *books {
err := n.Write([]string{book.Author,
book.Title})
if err != nil {
return err
}
}

n.Flush()
return n.Error()
}
  1. Add these additional functions to write_csv.go, as follows:
        // WriteCSVOutput initializes a set of books
// and writes them to os.Stdout
func WriteCSVOutput() error {
b := Books{
Book{
Author: "F Scott Fitzgerald",
Title: "The Great Gatsby",
},
Book{
Author: "J D Salinger",
Title: "The Catcher in the Rye",
},
}

return b.ToCSV(os.Stdout)
}

// WriteCSVBuffer returns a buffer CSV for
// a set of books
func WriteCSVBuffer() (*bytes.Buffer, error) {
b := Books{
Book{
Author: "F Scott Fitzgerald",
Title: "The Great Gatsby",
},
Book{
Author: "J D Salinger",
Title: "The Catcher in the Rye",
},
}

w := &bytes.Buffer{}
err := b.ToCSV(w)
return w, err
}
  1. Create a new directory named example and navigate to it.
  2. Create a main.go file with the following contents:
        package main

import (
"fmt"

"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/csvformat"
)

func main() {
if err := csvformat.AddMoviesFromText(); err != nil {
panic(err)
}

if err := csvformat.WriteCSVOutput(); err != nil {
panic(err)
}

buffer, err := csvformat.WriteCSVBuffer()
if err != nil {
panic(err)
}

fmt.Println("Buffer = ", buffer.String())
}
  1. Rungo run ..
  2. You may also run the following:
          $ go build
          
$ ./example

You should see the following output:

          $ go run . 
          
[]csvformat.Movie{csvformat.Movie{Title:"Guardians of the
Galaxy Vol. 2", Director:"James Gunn", Year:2017},
csvformat.Movie{Title:"Star Wars: Episode VIII", Director:"Rian
Johnson", Year:2017}}

Author,Title
F Scott Fitzgerald,The Great Gatsby
J D Salinger,The Catcher in the Rye
Buffer = Author,Title
F Scott Fitzgerald,The Great Gatsby
J D Salinger,The Catcher in the Rye
  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

In order to learn how to read a CSV format, we first represent our data as a structure. In Go, it's very useful to format data as a structure, as it makes things such as marshaling and encoding relatively simple. Our read example uses movies as our data type. The function takes anio.Readerinterface that holds our CSV data as an input. This could be a file or a buffer. We then use that data to create and populate aMoviestructure, including converting the year into an integer. We also add options to the CSV parser to use; (semi-colon) as the separator and- (hyphen) as a comment line.

Next, we explore the same idea, but in reverse. Novels are represented with a title and an author. We initialize an array of novels and then write specific novels in the CSV format to an io.Writer interface. Once again, this can be a file, stdout, or a buffer.

The CSV package is an excellent example of why you'd want to think of data flows in Go as implementing common interfaces. It's easy to change the source and destination of our data with small one-line tweaks, and we can easily manipulate CSV data without using an excessive amount of memory or time. For example, it would be possible to read from a stream of data one record at a time and write to a separate stream in a modified format one record at a time. Doing this would not incur significant memory or processor usage.

Later, when we explore data pipelines and worker pools, you'll see how these ideas can be combined and how to handle these streams in parallel.

Working with temporary files

We've created and made use of files for a number of examples so far. We've also had to manually deal with cleanup, name collision, and more. Temporary files and directories are a quicker, simpler way to handle these cases.

How to do it...

The following steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/tempfiles.
  2. Navigate to this directory.
  3. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/tempfiles

You should see a file called go.mod that contains the following contents:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/tempfiles    
  1. Copy the tests from~/projects/go-programming-cookbook-original/chapter1/tempfiles or use this as an exercise to write some of your own code!
  2. Create a file calledtemp_files.gowith the following contents:
        package tempfiles

import (
"fmt"
"io/ioutil"
"os"
)

// WorkWithTemp will give some basic patterns for working
// with temporary files and directories
func WorkWithTemp() error {
// If you need a temporary place to store files with
// the same name ie. template1-10.html a temp directory
// is a good way to approach it, the first argument
// being blank means it will use create the directory
// in the location returned by
// os.TempDir()
t, err := ioutil.TempDir("", "tmp")
if err != nil {
return err
}

// This will delete everything inside the temp file
// when this function exits if you want to do this
// later, be sure to return the directory name to the
// calling function
defer os.RemoveAll(t)

// the directory must exist to create the tempfile
// created. t is an *os.File object.
tf, err := ioutil.TempFile(t, "tmp")
if err != nil {
return err
}

fmt.Println(tf.Name())

// normally we'd delete the temporary file here, but
// because we're placing it in a temp directory, it
// gets cleaned up by the earlier defer

return nil
}
  1. Create a new directory named example and navigate to it.
  2. Create a main.go file with the following contents:
        package main

import "github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/tempfiles"

func main() {
if err := tempfiles.WorkWithTemp(); err != nil {
panic(err)
}
}
  1. Run go run ..
  2. You may also run the following:
          $ go build
$ ./example

You should see the following output (with a different path):

          $ go run . 
          
/var/folders/kd/ygq5l_0d1xq1lzk_c7htft900000gn/T
/tmp764135258/tmp588787953
  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

Creating temporary files and directories can be done using the ioutil package. Although you must still delete the files yourself, using RemoveAll is the convention, and it will do this for you with only one extra line of code.

When writing tests, the use of temporary files is highly recommended. It's also useful for things such as build artifacts and more. The Go ioutil package will try and honor the OS preferences by default, but it allows you to fall back to other directories if required.

Working with text/template and html/template

Go provides rich support for templates. It is simple to nest templates, import functions, represent variables, iterate over data, and so on. If you need something more sophisticated than a CSV writer, templates may be a great solution.

Another application for templates is for websites. When we want to render server-side data to the client, templates fit the bill nicely. At first, Go templates can appear confusing. This section will explore working with templates, collecting templates inside of a directory, and working with HTML templates.

How to do it...

These steps cover how to write and run your application:

  1. From your Terminal or console application, create a new directory called ~/projects/go-programming-cookbook/chapter1/templates.
  2. Navigate to this directory.
  1. Run the following command:
          $ go mod init github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/templates

You should see a file called go.mod that contains the following content:

module github.com/PacktPublishing/Go-Programming-Cookbook-Second-Edition/chapter1/templates
  1. Copy the tests from~/projects/go-programming-cookbook-original/chapter1/templates or use this as an exercise to write some of your own code!
  2. Create a file called templates.go with the following contents:
        package templates

import (
"os"
"strings"
"text/template"
)

const sampleTemplate = `
This template demonstrates printing a {{ .Variable |
printf "%#v" }}.

{{if .Condition}}
If condition is set, we'll print this
{{else}}
Otherwise, we'll print this instead
{{end}}

Next we'll iterate over an array of strings:
{{range $index, $item := .Items}}
{{$index}}: {{$item}}
{{end}}

We can also easily import other functions like
strings.Split
then immediately used the array created as a result:
{{ range $index, $item := split .Words ","}}
{{$index}}: {{$item}}
{{end}}

Blocks are a way to embed templates into one another
{{ block "block_example" .}}
No Block defined!
{{end}}


{{/*
This is a way
to insert a multi-line comment
*/}}
`

const secondTemplate = `
{{ define "block_example" }}
{{.OtherVariable}}
{{end}}
`
  1. Add a function to the end of templates.go, as follows:
        // RunTemplate initializes a template and demonstrates a 
// variety of template helper functions
func RunTemplate() error {
data := struct {
Condition bool
Variable string
Items []string
Words string
OtherVariable string
}{
Condition: true,
Variable: "variable",
Items: []string{"item1", "item2", "item3"},
Words:
"another_item1,another_item2,another_item3",
OtherVariable: "I'm defined in a second
template!",
}

funcmap := template.FuncMap{
"split": strings.Split,
}

// these can also be chained
t := template.New("example")
t = t.Funcs(funcmap)

// We could use Must instead to panic on error
// template.Must(t.Parse(sampleTemplate))
t, err := t.Parse(sampleTemplate)
if err != nil {
return err
}

// to demonstrate blocks we'll create another template
// by cloning the first template, then parsing a second
t2, err := t.Clone()
if err != nil {
return err
}

t2, err = t2.Parse(secondTemplate)
if err != nil {
return err
}

// write the template to stdout and populate it
// with data
err = t2.Execute(os.Stdout, &data)
if err != nil {
return err
}

return nil
}
  1. Create a file called template_files.go with the following contents:
        package templates

import (
"io/ioutil"
"os"
"path/filepath"
"text/template"
)

//CreateTemplate will create a template file that contains data
func CreateTemplate(path string, data string) error {
return ioutil.WriteFile(path, []byte(data),
os.FileMode(0755))
}

// InitTemplates sets up templates from a directory
func InitTemplates() error {
tempdir, err := ioutil.TempDir("", "temp")
if err != nil {
return err
}
defer os.RemoveAll(tempdir)

err = CreateTemplate(filepath.Join(tempdir, "t1.tmpl"),
`Template 1! {{ .Var1 }}
{{ block "template2" .}} {{end}}
{{ block "template3" .}} {{end}}
`)
if err != nil {
return err
}

err = CreateTemplate(filepath.Join(tempdir, "t2.tmpl"),
`{{ define "template2"}}Template 2! {{ .Var2 }}{{end}}
`)
if err != nil {
return err
}

err = CreateTemplate(filepath.Join(tempdir, "t3.tmpl"),
`{{ define "template3"}}Template 3! {{ .Var3 }}{{end}}
`)
if err != nil {
return err
}

pattern := filepath.Join(tempdir, "*.tmpl")

// Parse glob will combine all the files that match
// glob and combine them into a single template
tmpl, err := template.ParseGlob(pattern)
if err != nil {
return err
}

// Execute can also work with a map instead
// of a struct
tmpl.Execute(os.Stdout, map[string]string{
"Var1": "Var1!!",
"Var2": "Var2!!",
"Var3": "Var3!!",
})

return nil
}
  1. Create a file called html_templates.go with the following contents:
        package templates

import (
"fmt"
"html/template"
"os"
)

// HTMLDifferences highlights some of the differences
// between html/template and text/template
func HTMLDifferences() error {
t := template.New("html")
t, err := t.Parse("<h1>Hello! {{.Name}}</h1>n")
if err != nil {
return err
}

// html/template auto-escapes unsafe operations like
// javascript injection this is contextually aware and
// will behave differently
// depending on where a variable is rendered
err = t.Execute(os.Stdout, map[string]string{"Name": "
<script>alert('Can you see me?')</script>"})
if err != nil {
return err
}

// you can also manually call the escapers
fmt.Println(template.JSEscaper(`example
<example@example.com>`))
fmt.Println(template.HTMLEscaper(`example
<example@example.com>`))
fmt.Println(template.URLQueryEscaper(`example
<example@example.com>`))

return nil
}
  1. Create a new directory named example and navigate to it.
  2. Create a main.go file with the following contents:
        package main

import "github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter1/templates"

func main() {
if err := templates.RunTemplate(); err != nil {
panic(err)
}

if err := templates.InitTemplates(); err != nil {
panic(err)
}

if err := templates.HTMLDifferences(); err != nil {
panic(err)
}
}
  1. Run go run ..
  2. You may also run the following:
          $ go build
$ ./example

You should see the following output (with a different path):

  1. If you copied or wrote your own tests, go up one directory and run go test, and ensure that all tests pass.

How it works...

Go has two template packages: text/template and html/template. They share functionality and a variety of functions. In general, you should use html/template to render websites and text/template for everything else. Templates are plain text, but variables and functions can be used inside of curly brace blocks.

The template packages also provide convenience methods to work with files. The example that we used here creates a number of templates in a temporary directory and then reads them all with a single line of code.

The html/template package is a wrapper around the text/template package. All of the template examples work with the html/template package directly, using no modification and only changing the import statement. HTML templates provide the added benefit of context-aware safety; this prevents security breaches such as JavaScript injection.

The template packages provide what you'd expect from a modern template library. It's easy to combine templates, add application logic, and ensure safety when emitting results to HTML and JavaScript.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Develop applications for different domains using modern programming techniques
  • Tackle common problems when it comes to parallelism, concurrency, and reactive programming in Go
  • Work with ready-to-execute code based on the latest version of Go

Description

Go (or Golang) is a statically typed programming language developed at Google. Known for its vast standard library, it also provides features such as garbage collection, type safety, dynamic-typing capabilities, and additional built-in types. This book will serve as a reference while implementing Go features to build your own applications. This Go cookbook helps you put into practice the advanced concepts and libraries that Golang offers. The recipes in the book follow best practices such as documentation, testing, and vendoring with Go modules, as well as performing clean abstractions using interfaces. You'll learn how code works and the common pitfalls to watch out for. The book covers basic type and error handling, and then moves on to explore applications, such as websites, command-line tools, and filesystems, that interact with users. You'll even get to grips with parallelism, distributed systems, and performance tuning. By the end of the book, you'll be able to use open source code and concepts in Go programming to build enterprise-class applications without any hassle.

Who is this book for?

If you're a web developer, programmer, or enterprise developer looking for quick solutions to common and not-so-common problems in Go programming, this book is for you. Basic knowledge of the Go language is assumed.

What you will learn

  • Work with third-party Go projects and modify them for your use
  • Write Go code using modern best practices
  • Manage your dependencies with the new Go module system
  • Solve common problems encountered when dealing with backend systems or DevOps
  • Explore the Go standard library and its uses
  • Test, profile, and fine-tune Go applications

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jul 19, 2019
Length: 434 pages
Edition : 2nd
Language : English
ISBN-13 : 9781789800982
Vendor :
Google
Category :
Languages :

What do you get with a Packt Subscription?

Free for first 7 days. $19.99 p/m after that. Cancel any time!
Product feature icon Unlimited ad-free access to the largest independent learning library in tech. Access this title and thousands more!
Product feature icon 50+ new titles added per month, including many first-to-market concepts and exclusive early access to books as they are being written.
Product feature icon Innovative learning tools, including AI book assistants, code context explainers, and text-to-speech.
Product feature icon Thousands of reference materials covering every tech concept you need to stay up to date.
Subscribe now
View plans & pricing

Product Details

Publication date : Jul 19, 2019
Length: 434 pages
Edition : 2nd
Language : English
ISBN-13 : 9781789800982
Vendor :
Google
Category :
Languages :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just S$6 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just S$6 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total S$ 208.97
Mastering Go
S$88.99
Go Programming Cookbook
S$59.99
Hands-On System Programming with Go
S$59.99
Total S$ 208.97 Stars icon

Table of Contents

15 Chapters
I/O and Filesystems Chevron down icon Chevron up icon
Command-Line Tools Chevron down icon Chevron up icon
Data Conversion and Composition Chevron down icon Chevron up icon
Error Handling in Go Chevron down icon Chevron up icon
Network Programming Chevron down icon Chevron up icon
All about Databases and Storage Chevron down icon Chevron up icon
Web Clients and APIs Chevron down icon Chevron up icon
Microservices for Applications in Go Chevron down icon Chevron up icon
Testing Go Code Chevron down icon Chevron up icon
Parallelism and Concurrency Chevron down icon Chevron up icon
Distributed Systems Chevron down icon Chevron up icon
Reactive Programming and Data Streams Chevron down icon Chevron up icon
Serverless Programming Chevron down icon Chevron up icon
Performance Improvements, Tips, and Tricks Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.4
(11 Ratings)
5 star 45.5%
4 star 9.1%
3 star 9.1%
2 star 9.1%
1 star 27.3%
Filter icon Filter
Top Reviews

Filter reviews by




Amazon Customer Sep 29, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
With this practical guide, now i can tackle the trickiest problems of the problems in the Go programming. A must have book.
Amazon Verified review Amazon
Jeffrey Yoo Oct 06, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This textbook distinguishes itself from other Go textbooks by explaining important fundamentals in Go (I/O operations, setting up http clients, etc.) while also providing many practical implementations of those fundamentals. Even the simpler lessons like error handling in Go have a nice practical twist to them by also including how to handle the logging of the error and what the norms are of handling long running panics. Another thing that I valued from this book were the examples of how to use some industry standard third party Go libraries and packages especially within the testing section. Having a reference guide of how to use some commonly used test runners and how to write BDD test in Go is always nice to have. All in all, this book is a great source of examples that reflect current Go industry best practices and how to write tests for those practices.
Amazon Verified review Amazon
Brian Stinar Aug 20, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Super helpful book for improving my Go - I hope to use what I'm learning in building an IoT data collector
Amazon Verified review Amazon
Jack Xie Dec 10, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I have been using this book for reference and studying for over 3 month. This is book has very good structure to teach you every aspect of Golang language. The author also provide excellent examples for you to go through every topic. At the end of each topic, the author also gave an insight of why golang solve the a problem this way. A must have to golang programers! Highly recommend!
Amazon Verified review Amazon
Ingo Jaeckel Oct 08, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This is a great resource for almost any facet of Go development and a handy tool in the arsenal of the busy Go developer.Also, if you are solid in other languages and want to learn Go quickly, this book's conciseness & breadth might speed up your learning process.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is included in a Packt subscription? Chevron down icon Chevron up icon

A subscription provides you with full access to view all Packt and licnesed content online, this includes exclusive access to Early Access titles. Depending on the tier chosen you can also earn credits and discounts to use for owning content

How can I cancel my subscription? Chevron down icon Chevron up icon

To cancel your subscription with us simply go to the account page - found in the top right of the page or at https://subscription.packtpub.com/my-account/subscription - From here you will see the ‘cancel subscription’ button in the grey box with your subscription information in.

What are credits? Chevron down icon Chevron up icon

Credits can be earned from reading 40 section of any title within the payment cycle - a month starting from the day of subscription payment. You also earn a Credit every month if you subscribe to our annual or 18 month plans. Credits can be used to buy books DRM free, the same way that you would pay for a book. Your credits can be found in the subscription homepage - subscription.packtpub.com - clicking on ‘the my’ library dropdown and selecting ‘credits’.

What happens if an Early Access Course is cancelled? Chevron down icon Chevron up icon

Projects are rarely cancelled, but sometimes it's unavoidable. If an Early Access course is cancelled or excessively delayed, you can exchange your purchase for another course. For further details, please contact us here.

Where can I send feedback about an Early Access title? Chevron down icon Chevron up icon

If you have any feedback about the product you're reading, or Early Access in general, then please fill out a contact form here and we'll make sure the feedback gets to the right team. 

Can I download the code files for Early Access titles? Chevron down icon Chevron up icon

We try to ensure that all books in Early Access have code available to use, download, and fork on GitHub. This helps us be more agile in the development of the book, and helps keep the often changing code base of new versions and new technologies as up to date as possible. Unfortunately, however, there will be rare cases when it is not possible for us to have downloadable code samples available until publication.

When we publish the book, the code files will also be available to download from the Packt website.

How accurate is the publication date? Chevron down icon Chevron up icon

The publication date is as accurate as we can be at any point in the project. Unfortunately, delays can happen. Often those delays are out of our control, such as changes to the technology code base or delays in the tech release. We do our best to give you an accurate estimate of the publication date at any given time, and as more chapters are delivered, the more accurate the delivery date will become.

How will I know when new chapters are ready? Chevron down icon Chevron up icon

We'll let you know every time there has been an update to a course that you've bought in Early Access. You'll get an email to let you know there has been a new chapter, or a change to a previous chapter. The new chapters are automatically added to your account, so you can also check back there any time you're ready and download or read them online.

I am a Packt subscriber, do I get Early Access? Chevron down icon Chevron up icon

Yes, all Early Access content is fully available through your subscription. You will need to have a paid for or active trial subscription in order to access all titles.

How is Early Access delivered? Chevron down icon Chevron up icon

Early Access is currently only available as a PDF or through our online reader. As we make changes or add new chapters, the files in your Packt account will be updated so you can download them again or view them online immediately.

How do I buy Early Access content? Chevron down icon Chevron up icon

Early Access is a way of us getting our content to you quicker, but the method of buying the Early Access course is still the same. Just find the course you want to buy, go through the check-out steps, and you’ll get a confirmation email from us with information and a link to the relevant Early Access courses.

What is Early Access? Chevron down icon Chevron up icon

Keeping up to date with the latest technology is difficult; new versions, new frameworks, new techniques. This feature gives you a head-start to our content, as it's being created. With Early Access you'll receive each chapter as it's written, and get regular updates throughout the product's development, as well as the final course as soon as it's ready.We created Early Access as a means of giving you the information you need, as soon as it's available. As we go through the process of developing a course, 99% of it can be ready but we can't publish until that last 1% falls in to place. Early Access helps to unlock the potential of our content early, to help you start your learning when you need it most. You not only get access to every chapter as it's delivered, edited, and updated, but you'll also get the finalized, DRM-free product to download in any format you want when it's published. As a member of Packt, you'll also be eligible for our exclusive offers, including a free course every day, and discounts on new and popular titles.