Using the bytes and strings packages
The bytes
and string
packages have a number of useful helpers to work with and convert between strings and byte types. They allow the creation of buffers that work with a number of common I/O interfaces.
Getting ready
Refer to the Getting ready section's steps in the Using the common I/O interfaces recipe.
How to do it...
These steps cover writing and running your application:
- From your terminal/console application, create a new directory called
chapter1/bytestrings
. - Navigate to this directory.
- Copy tests from https://github.com/agtorre/go-cookbook/tree/master/chapter1/bytesstrings, or use this as an exercise to write some of your own code!
- Create a file called
buffer.go
with 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 intial 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 }
- Create a file called
bytes.go
with 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 tokenzation scanner := bufio.NewScanner(reader) scanner.Split(bufio.ScanWords) // iterate over all of the scan events for scanner.Scan() { fmt.Print(scanner.Text()) } return nil }
- 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 stringn" r := strings.NewReader(s) // prints s on Stdout io.Copy(os.Stdout, r) }
- Create a new directory named
example
. - Navigate to
example
. - Create a
main.go
file with the following contents and ensure that you modify the interfaces imported to use the path you set up in step 2:
package main import "github.com/agtorre/go-cookbook/chapter1/bytestrings" func main() { err := bytestrings.WorkWithBuffer() if err != nil { panic(err) } // each of these print to stdout bytestrings.SearchString() bytestrings.ModifyString() bytestrings.StringReader() }
- Run
go run main.go
. - You may also run these:
go build ./example
You should see the following output:
$ go run main.go 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
- If you copied or wrote your own tests, go up one directory and run
go test
, and ensure 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 you can take advantage of ioutil
functions to manipulate the data. For steaming 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 between interfaces with these basic types--it's relatively simple to convert between strings and bytes. When working with strings, the strings
package 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, the strings
and strconv
packages are sufficient. The strings
package allows you to make a string look like a title, split it into an array, or trim whitespace. It also provides a Reader
interface of its own that can be used instead of the bytes
package reader type.