The most useful string functions in GO

The concept of a string exists in all languages and Golang is not the exception.
In this blog, I’m going to show the most useful concepts and functions to work with strings in Golang.

Most of the examples will contain tests, you may be more used to using the Assert keyword to perform checking, but the authors of The Go Programming Language make some good arguments for Go’s style over Assertions

In Golang, String is a data type, it is not a struct or class. It does not have methods or functions.

Different from other languages such as Java, in Golang the strings are not an array of characters, instead, they are an array of bytes that represent UTF-8 encodings. That means you are able to use any kind of human language by default. I am going to show the differences

func TestStringSliceBytesUTF8(t *testing.T) {
  got := "Hello δΈ–η•Œ 🦊"
  expected := "\x48\x65\x6c\x6c\x6f\x20\xe4\xb8\x96\xe7\x95\x8c\x20\xf0\x9f\xa6\x8a"

  if got != expected {
    t.Errorf("expected %s got %s", expected, got)
  }
}

The main difference between string and slice of bytes in GO is the way they are handled in memory. Strings are immutable, you are not able to modify them. If you want to add a new element, you have to create another string.

String has no methods to split or get the length, instead, Golang gives up the strings package to manipulate

Basically, you have two ways to create a string.
From string literal

func TestStringLiteral(t *testing.T) {
   got := "Go is Awesome!"
   if got != "Go is Awesome!" {
       t.Errorf("expected %s got %s", "Go is Awesome!", got)
   }
}

From string multiline

func TestStringMultiline(t *testing.T) {
   got := `
   {
       "state":"Awesome",
       "data":[
          {
             "language":"Go"
          }
       ]
    }`
 
   expect := "\n" +
       "\t{\n" +
       "\t\t\"state\":\"Awesome\",\n" +
       "\t\t\"data\":[\n" +
       "\t\t   {\n" +
       "\t\t\t  \"language\":\"Go\"\n" +
       "\t\t   }\n" +
       "\t\t]\n" +
       "\t }"
 
   if got != expect {
       t.Errorf("expected %s got %s", expect, got)
   }
}
 

The length of a string can be obtained using the standard function len(s)

func TestStringLength(t *testing.T) {
 s := "golang"
   if len(s) != 6 {
       t.Error("Error", s)
   }
}

It is important to remember that a string is an array of bytes, the function len(s) is going to return the amount of bytes used. So for non-ASCII characters you are going to obtain something like that:

func TestStringLength(t *testing.T) {
   s := "вишня"
   if len(s) != 10 {
       t.Error("Error", len(s))
   }
 
   s = "🐺🦊🦝"
   if len(s) != 12 {
       t.Error("Error", len(s))
   }
}

You have to see a string as an array of bytes, so in order to get an element by index do this:

func TestStringElementAt(t *testing.T) {
   myString := "Go is Awesome!"
   //at index 0, it is a byte
   b := myString[0]
 
   //convert byte to rune, rune is a character
   got := rune(b)
 
   expect := 'G'
   if got != expect {
       t.Errorf("expect %c got %c", expect, b)
   }
}

the result is a byte, you need to convert it to a character using rune type. Here is more information about rune type https://golangbyexample.com/understanding-rune-in-golang/

The same, you have to see a string as an array of bytes, simply you are able to iterate it

func TestStringIterationWithRange(t *testing.T) {
   nameString := "omar barra"
   for _, v := range namestring {
       fmt.Printf("%q\n", v)
   }
}

Go does not provide a function or method to compare two strings, it is not necessary in golang. You can use the operators == or != directly.

func TestStringEqualsCaseSensitive(t *testing.T) {
   got := "Go is Awesome!"
   expect := "Go is Awesome!"
 
   if got != expect {
       t.Errorf("expected %s got %s", expect, got)
   }
 
   if got == "other text" {
       t.Errorf("expected %s got %s", expect, got)
   }
}

Fortunately, Go offers you a function for case-insensitive equality, which is strings.EqualFold, there we go

func TestStringEqualsCaseInsensitive(t *testing.T) {
   got := "Go is Awesome!"
   expect := "go is awesome!"
  
   if !strings.EqualFold(got, expect) {
       t.Errorf("expected %s got %s", expect, got)
   }
}

Simply do this

func TestStringConcat(t *testing.T) {
   result := "First" + "Second"
 
   if result != "FirstSecond" {
       t.Error("Error", result)
   }
}

Remember you are creating a new string and you are allocating a new portion of memory.

strings.Builder

In some use cases, you have to concatenate a variable list of strings. When you have a variable list of strings the most appropriate way to concatenate is using string.Builder. This structure gives you the function to concatenate strings and avoid wasting memory unnecessarily.

func TestStringBuilder(t *testing.T) {
   words := []string{"First", "Second", "Third"}
 
   var builder strings.Builder
 
   for _, w := range words {
       builder.WriteString(w)
   }
 
   result := builder.String()
 
   if result != "FirstSecondThird" {
       t.Error("Error", result)
   }
}

IsBlank

This function is quite useful and many languages, for example, Java provides one. However, Go does not. Here are two ways to do that

 
func TestStringIsBlank(t *testing.T) {
   myString := "\n\n\t\t"
 
   //is blank first option
   if strings.TrimSpace(myString) != "" {
       t.Error("Error ", myString)
   }
 
   //is blank second option
   if len(strings.TrimSpace(myString)) != 0 {
       t.Error("Error ", myString)
   }
}

Removing characters from a string

Sometimes, you need to remove one or more characters from a string. The Go way to do it is:

 
func TestStringRemovingSpace(t *testing.T) {
   myString := "I am Programmer"
   r := strings.Replace(myString, " ", "", -1)
   if r != "IamProgrammer" {
       t.Error("Error", r)
   }
}

This function will replace every space in the string with β€œβ€.

A most readily way to do it is using strings.ReplaceAll introduced in GO 1.13

func TestStringRemovingSpace(t *testing.T) {
   myString := "I am Programmer"
   r := strings.ReplaceAll(myString, " ", "")
   if r != "IamProgrammer" {
       t.Error("Error", r)
   }
}

Golang’s main goal is to be as simple and powerful as possible. In that way, Go provides the most necessary function to work with string avoiding fancy things such as a function to compare string, function to check string blank.

In this article, I do not cover how to join or split a string, string formatting and other useful functions such as strings.Trim, strings.Prefix, strings.Sufix. but you can find those examples and more in my repository

https://github.com/obarra-dev/poc-golang/blob/master/go-115/string_test.go

Thanks, see you in the next episode.