Golang: String Manipulation
Tags: go, 100daysofgolang, webdev
Published: 2022-06-28 18:17:42

## Introduction 

In the 15th post of the Series, we will be looking into the details of the String manipulation and performing types of operations in Golang. We will explore string manipulation, concatenation, helper functions, etc. which will help in working with strings in Golang. 

## String Concatenation

String Concatenation refers to the combining and formatting of strings in Golang. We can combine multiple strings and formating the way we display the strings in Golang. We have a few ways and functions to concatenate strings in Golang.

### Using the + operator

We can simply concatenate strings using the `+` operator, though keep in mind you should only concatenate the string with a string and not any other data type like integer, or float, it will throw out errors for mismatched string types.

```go
package main

import (
    "fmt"
)
func main() {
    s1 := "Go"
    s2 := "Programming"
    s3 := "Language"

    s := s1 + s2 + s3
    fmt.Println(s)
}
```

```
$ go run concatenate.go

GoProgrammingLanguage

```

The `+` operator will literally join the strings as it is and form a string. 

### Using the += operator

The other way to continuously append a string to an existing string, we can use the `+=` operator. This operator will append the provided string to the end of the original string. 

```go
p := "Story"
p += "Book"
fmt.Println(p)
```   

```
go run concatenate.go

StoryBook
```

### Using the Join method 

The join method is a function available in the string package in Golang. We can join strings elements in a slice or an array using the [Join](https://pkg.go.dev/strings#Join) method in the strings [package](https://pkg.go.dev/strings) in golang. The Join method will combine all the elements in between the elements with a particular string. So, the function takes two parameters `Join(array, string)`, the array or a slice is parsed into the function which will be used to insert the provided string in between the elements of the slice.

#### Join

- Parameters   : array/slice, string
- Return Value : string

```go
q := []string{"meetgor.com", "tags", "golang", "string"}
r := strings.Join(q, "/")
fmt.Println(r)
```   

```
go run concatenate.go

meetgor.com/tags/golang/string
```

   In the above example, we use have used the `Join` method to insert a string in between the elements of a slice of strings. The string `"/"` has been inserted in between the elements, and the elements are combined as a single string. So, each individual element starting from the `0` index `meetgor.com` is appended the string `/` and further the next element `tags` have been appended and the procedure caries on till the last element. Note that the string is not inserted after the last element. The function `Join` returns a string and thereby we store the string in a variable.

### Using Sprintf method

We can use the [Sprintf](https://pkg.go.dev/fmt#Sprintf) function from the fmt package to format the string by storing the string rather than printing it to the console. The sprintf function is quite similar to the `Printf` but it only parses strings rather than printing them directly to the console.

```go
// Using Sprintf function to format strings

name := "peter"
domain := "telecom"
service := "ceo"

email := fmt.Sprintf("%s.%s@%s.com", service, name, domain)
fmt.Println(email)
```   

```
go run concatenate.go

ceo.peter@telecom.com
```

The sprintf function basically allows us to concatenate strings in a defined format just like we use `printf` to print formatted strings. In the above example, we have formatted three strings in the form of an email by assigning a placeholder for the string i.e. `%s`, and adding the required characters in the formatted string.

### Using Go string Builder method

The [Builder](https://pkg.go.dev/strings#Builder) type is provided by the strings package in Golang. The Builder type helps in building strings in an efficient way. By creating a string builder object, we can perform operations on a String.

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
  // Using Builder function

  c := []string{"j", "a", "v", "a"}
  var builder strings.Builder
  for _, item := range c {
    builder.WriteString(item)
  }
  fmt.Println("builder = ", builder.String())
  b := []byte{'s', 'c', 'r', 'i', 'p', 't'}
  for _, item := range b {
    builder.WriteByte(item)
  }
  fmt.Println("builder = ", builder.String())
  builder.WriteRune('s')
  fmt.Println("builder = ", builder.String())
  fmt.Println("builder = ", builder)
}
```   

```
go run concatenate.go

builder =  java
builder =  javascript
builder =  javascripts
builder =  {0xc000088dd8 [106 97 118 97 115 99 114 105 112 116 115]}
```

   The builder structure provided by the strings package is quite important for working with strings in an efficient manner. Its usually used for string concatenation operations. We can perform write operations to the buffer which is a byte slice.  Here we have created the `builder` variable which is of type `strings.Builder`, further we have appended the string to it in a for a loop. So, we construct a string from the string list elements, they can be even rune slice or byte slice.
   We have used three methods here, the `WriteString`, `WriteByte`, and `WriteRune` which are quite obliviously used for writing `string`, `byte`, and `rune`to the string builder object.
   

### Using the Bytes buffer method

The `bytes` package also has something similar to `Builder` type in `string` as [Buffer](https://pkg.go.dev/bytes#Buffer.Bytes). It has almost the same set of methods and properties. The main difference is the efficiency, `strings.Builder` is comparatively faster than the `bytes.Buffer` type due to several low-level implementations. We can discuss those fine details in a separate article but right now we'll focus on the ways we can utilize this type for string concatenation.

```go
package main

import (
	"fmt"
	"bytes"
)

func main() {
	// Using bytes buffer method

	var buf bytes.Buffer

	for i := 0; i < 2; i++ {
		buf.WriteString("ja")
	}
	fmt.Println(buf.String())

	buf.WriteByte('r')

	fmt.Println(buf.String())

	k := []rune{'s', 'c', 'r', 'i', 'p', 't'}
	for _, item := range k {
		buf.WriteRune(item)
	}
	fmt.Println(buf.String())
}
```

```
go run concatenate.go

jaja
jajar
jajarscript
{[106 97 106 97 114 115 99 114 105 112 116] 0 0}
```

   So, like for the `strings.Builder` type, we have [WriteString](https://pkg.go.dev/bytes#Buffer.WriteString), [WriteByte](https://pkg.go.dev/bytes#Buffer.WriteByte), and [WriteRune](https://pkg.go.dev/bytes#Buffer.WriteRune) in the `bytes.Buffer` type. We can use it exactly the way we do with the previous example. Also, the `bytes.Buffer` type returns a slice of bytes so we will have to use the [String()](https://pkg.go.dev/strings#Builder.String) method to format it as a string.

   If we look at the [bytes.Buffer](https://go.dev/src/bytes/buffer.go) type, it returns a slice of bytes and two more properties viz. `off` and `lastRead`. These two properties are used for indicating the index of the byte in the buffer and reallocation of the buffer. This is too low-level stuff that can be explored and explained in a separate section. For further readings on the bytes Buffer or String Builder types, you can follow up with these articles: 

   - [bytes.Buffer vs strings.Builder](https://medium.com/@felipedutratine/string-concatenation-in-golang-since-1-10-bytes-buffer-vs-strings-builder-2b3081848c45)
   - [Bytes Buffer](https://syslog.ravelin.com/bytes-buffer-i-thought-you-were-my-friend-4148fd001229)
   - [Best ways to use bytes.Buffer](https://stackoverflow.com/a/49295215)


## String Comparison

Now, we can move into the comparison of Strings in Golang. We have quite a few ways to compare strings in golang. We cover some of them in this section.

### Using Comparison operators

The basic comparison can be done with the comparison operators provided by Golang. Just like we compare numeric data we can compare strings. Though the factor with which we compare them is different. We compare them by the lexical order of the string characters.

```go
package main

import "fmt"

func main() {
	s1 := "gopher"
	s2 := "Gopher"
	s3 := "gopher"

	isEqual := s1 == s2

  //"gopher" is not same as "Gopher" and hence `false`
	fmt.Printf("S1 and S2 are Equal? %t \n", isEqual)
	fmt.Println(s1 == s2)

  // "gopher" is not equal to "Gopher" and hence `true`
	fmt.Println(s1 != s2)

  // "Gopher" comes first lexicographically than "gopher" so return true 
  // G -> 71 in ASCII and g -> 103
	fmt.Println(s2 < s3)
	fmt.Println(s2 <= s3)

  // "Gopher" is not greater than "gopher" as `G` comes first in ASCII table
  // So G value is less than g i.e. 71 > 103 which is false
	fmt.Println(s2 > s3)
	fmt.Println(s2 >= s3)

}
```   

```
go run comparison.go

S1 and S2 are Equal? false 
false
true
true
true
false
false
```

In the above examples, we are able to see the comparison of two strings. There are three strings, two of which are identical, and the third is identical as well but is not equal considering the case of the characters in the string. We have compared the strings in order of the ASCII value of the characters of the strings. For example, A (65) comes before a (97). Similarly, numbers come before letters. So accordingly the comparison of these string characters decides the result.

For the ASCII table, you can take a look over the below image:

![ASCII Table](https://res.cloudinary.com/techstructive-blog/image/upload/v1656423519/blog-media/ascii_table.png)

### Using Compare method

We also have the [Compare](https://res.cloudinary.com/techstructive-blog/image/upload/v1656423519/blog-media/ascii_table.png) method in the strings package for comparing two strings. The comparison method returns an integer value of either -1, 0, or 1. If the two strings are equal, it will return 0. Else if the first string is lexicographically smaller than the second string, it will return -1, else it will return +1.
 

#### strings.Compare 

- Return Type: Int (-1, 0, 1)
- Parameters: string, string

You can check out the [source code](https://go.dev/src/strings/compare.go) for further clarity.

```go
package main

import(
  "fmt"
  "strings"
)

func main() {
	s1 := "gopher"
	s2 := "Gopher"
	s3 := "gopher"

	fmt.Println(strings.Compare(s1, s2))
	fmt.Println(strings.Compare(s1, s3))
	fmt.Println(strings.Compare(s2, s3))
}
```   

```
go run comparison.go

1
0
-1
```

   In the above example, the two strings `s1` and `s2` are compared and it returns the integer value `+1`, indicating the first string is lexicographically greater than the second string which is true `"gopher"` will be lexicographically after `"Gopher"` due to the presence of `G`.

   In the second example, we are comparing the strings `s1` and `s3` which are equal, and hence the function returns `0` as expected.

   In the third example, we are comparing the strings `s2` and `s3` identical to the first case but here order matters. We are comparing `"Gopher"` with `"gopher"` so the first string is lexicographically smaller than the second string and thereby returning `-1` as discussed.

### Using strings EqualFold

We also have another method in the strings library called [EqualFold](https://pkg.go.dev/strings#EqualFold) which compares two strings lexicographically but without considering the case precedence. That is the upper case or lower case is ignored and considered equal. So we are truly case-insensitively comparing the strings.

#### strings.EqualFold

- Return Type: bool (true or false)
- Parameters: string, string

```go
package main

import(
  "fmt"
  "strings"
)

func main() {

	s1 := "gopher"
	s2 := "Gopher"
	s3 := "gophy"

	fmt.Println(strings.EqualFold(s1, s2))
	fmt.Println(strings.EqualFold(s1, s3))
	fmt.Println(strings.EqualFold(s2, s3))
}
```

```
go run comparison.go

true
false
false
```

   So, in the above example, we are comparing the strings `"gopher"` and `"Gopher"` i.e. `s1` and `s2`, which are equal if we think case-insensitively. Hence the method returns true, they are equal.
   In the next example, we compare the strings, `s1` and `s3` i.e. `"gopher"` `"gophy"` which are not equal, and hence we return `false`. Similar is the case for `"Gopher"` and `"gophy"` which is `false`. Also, if we consider two strings `"gophy"` and `"gophy"` it will quite obliviously return `true`.

## String Manipulation and utility methods

The strings package in golang has some great utility methods for working with string or any form of text. We will explore some of the quite useful and widely used utilities in the strings package.

### ToLower, ToUpper and Title functions

The strings package also provides some utility functions for operating on the case of the characters in the strings. We have functions like [ToLower](https://pkg.go.dev/strings#ToLower), [ToUpper](https://pkg.go.dev/strings#ToUpper), and [Title](https://pkg.go.dev/golang.org/x/text/cases#Title) which can be used to convert the string into lower case, uppercased or Capitalised(Title) cased characters respectively.

#### strings.ToLower

- Return Type: string 
- Parameters: string


#### strings.ToUpper

- Return Type: string 
- Parameters: string


#### cases.Title

- Return Type: Caser
- Parameters: Language Options 

```go
package main

import (
	"fmt"
	"strings"

	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

func main() {
	s1 := "Ubuntu 22"
	s2 := "meet"
	s3 := "IND"
	fmt.Println(strings.ToLower(s1))
	fmt.Println(strings.ToLower(s2))
	fmt.Println(strings.ToLower(s3))

	fmt.Printf("\n")
	fmt.Println(strings.ToUpper(s1))
	fmt.Println(strings.ToUpper(s2))
	fmt.Println(strings.ToUpper(s3))

	fmt.Printf("\n")
	cases := cases.Title(language.English)
	fmt.Println(cases.String(s1))
	fmt.Println(cases.String(s2))
	fmt.Println(cases.String(s3))
}
```

```
# 100-days-of-golang/scripts/strings

go mod init
go get golang.org/x/text/cases
go get golang.org/x/text/language

go run utility.go                                                                                             
ubuntu 22
meet
ind

UBUNTU 22
MEET
IND

Ubuntu 22
Meet
Ind
```

   Here, we can see that the function, `ToLower` has converted all the characters of a string to the lower case of the alphabet. Similarly, the `ToUpper` function has turned the characters of the strings to their respective alphabetical upper case. 

   The [Title](https://pkg.go.dev/strings#Title) method in the strings package has been deprecated due to incompatibility with certain languages and cases. So, we are using the [text/cases](https://pkg.go.dev/golang.org/x/text/cases) package to get the [Title](https://pkg.go.dev/strings#Title) method that appropriately converts a string to Title cased. 
  To set up this function, you need to perform a certain package installation process which is quite straightforward. Just create a go mod which is used for managing dependencies and packages for a project. So run the commands given below in the same order in your local setup:

```
go mod init
go get golang.org/x/text/cases
go get golang.org/x/text/language
```
   This will set up a go.mod file and install the packages namely the `cases` and `language` packages. After doing this you will be able to access the functions `Title` from the cases package which can be imported by the format `"golang.org/x/text/cases"` and `"golang.org/x/text/language"`. Now, we can use the Title function and parse the parameters which is the language type. Here we have used the `language.English` which is a [language Tag](https://pkg.go.dev/golang.org/x/text@v0.3.7/language#Tag) to say use the semantics of English language while parsing the title cased characters. We now assign the value of the function `Title` to a variable as it will be of type `Caseer` and we want to still parse the string into the function. The [caser](https://pkg.go.dev/golang.org/x/text/cases#Caser) object will have certain methods and properties attached to it, we will use the method [Strings](https://pkg.go.dev/golang.org/x/text/cases#Caser.String) that will convert the given string into the title cased string. Hence we return the title cased string using the title function with the help of cases and language packages.

### Contains and ContainsAny functions

In the strings package, we have the [Contains](https://pkg.go.dev/strings#Contains) and [ContainsAny](https://pkg.go.dev/strings#ContainsAny) method which checks for the presence of substrings within a string. This will help in looking up hidden patterns and find for substrings in a string.

#### strings.Contains

- Parameters: string, string
- Return Type: bool (true, false)

The Contains method helps in getting the exact match of the substring in the given string. Firstly, the method takes two parameters one being the actual string and the other being the substring that you want to find inside the string. Let's say we have the `string="bootcamp"` and `substr="camp"`, then the `Contains` method will return true because the string contains the substring `camp`.

#### strings.ContainsAny

- Parameters: string, string
- Return Type: bool (true, false)

The `ContainsAny` method just like the Contains method takes two parameters string and the other as the substring, but it would return true if it finds any character or a single byte(Unicode chars) inside the string. Let's say the `string="google photos"` and `substring="abcde"`, then the `ContainsAny` method will return true because the string contains at least one character in the substring which is `e`. 

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	str := "javascript"
	substr := "script"
	s := "python"

	fmt.Println(strings.Contains(str, substr))
	fmt.Println(strings.Contains(str, s))

	fmt.Println(strings.ContainsAny(str, "joke"))
	fmt.Println(strings.ContainsAny(str, "xyz"))
	fmt.Println(strings.ContainsAny(str, ""))
}
```

Here, we have used the string methods like `Contains` and `ContainsAny` to find the substring inside a string. In the first example, the `str` variable is assigned as `"javascript"` and `substr` string as `"script"`. We use the `Contains` function with parameters `(str, substr)`. This will return `true` as we can see the `"script"` is a substring of `"javascript"`. Also, we have initialized the variable `s` to `"python"`. We use the `Contains` method with the parameters `(str, s)` which will return `false` as `"python"` is not a substring of `"javascript"`. 

The next set of examples is of the `ContainsAny` method which will return true for any character present in the substring is present in the string :). Quite a Simple right, Just understand that any character in your substring present in the string will return `true`. As we see in the example, `ContainsAny` method is used with the parameters  `("javascript", "joke")`, It will return `true` as `j` is present inside the string, though the entire substring is not present. 

The next example is by passing `("javascript", "xyz")` to the method `ContainsAny` will return `false` as we don't have either `"x"`, `"y"`, or `"z"` in the string. So this is how the `ContainsAny` method works. 

Other similar methods you might be interested in learning are: [Index](https://pkg.go.dev/strings#Index), [IndexAny](https://pkg.go.dev/strings#IndexAny), [LastIndex](https://pkg.go.dev/strings#LastIndex), etc. you can find the list of methods in the [strings package](https://pkg.go.dev/strings#pkg-functions).

### Split and SplitAfter functions

We also have methods to manipulate the string for splitting the characters or bytes with certain patterns. In the strings package, the [Split](https://pkg.go.dev/strings#Split) and [SplitAfter](https://pkg.go.dev/strings#SplitAfter) methods are quite handy to know about.

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Split method for splitting string into slice of string
	link := "meetgor.com/blog/golang/strings"
	fmt.Println(strings.Split(link, "/"))
	fmt.Println(strings.SplitAfter(link, "/"))

	// SplitAfter method for splitting string into slice of string with the pattern
	data := "200kms50kms120kms"
	fmt.Println(strings.Split(data, "kms"))
	fmt.Println(strings.SplitAfter(data, "kms"))
}
```

```
go run utility.go

[meetgor.com blog golang strings]
[meetgor.com/ blog/ golang/ strings]
[200 50 120 ]
[200kms 50kms 120kms ]
```

   So, from the above examples, we can see how `Split` and `SplitAfter` methods work. The Split method splits the text before and after the pattern string also removing the pattern string whereas the `SplitAfter` method keeps the pattern and splits it after it, hence we see the pattern string getting attached to the left string.

   In the first example, we see the `link` variable being split into strings as `"/"` being the separator. The Split method returns the slice of the string elements which have been split. In the `data` variable, the `"kms"` is the separator, so we get the resultant slice as `[200, 50, 120]`, the `"kms"` string acts as a separator and it is ignored.

   In the next example, we see the `link` variable being split into strings as `"/"` being the separator as previously but here, the splitting occurs after the separator has been parsed. So, we see `"meetgor/"` being split after the separator string and then `"blog/"` and so on. Also the next example, in the `data` variable, we see `"200kms"` being split instead of `200` using Split.

### Repeat and Fields functions

In the strings package, we have methods like [Repeat](https://pkg.go.dev/strings#Repeat) and [Fields](https://pkg.go.dev/strings#Fields) for manipulating the text inside the string. These methods are used to populate or extract data from the string. 

#### strings.Repeat

- Parameters: string, int
- Return Type: string

The `Repeat` method is used to create a string by repeating it n number of times and appending it to the string which is returned as the final string. So, the `Repeat` method takes in two parameters the string to repeat, an integer as a count to repeat the string, and returns a string.

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Repeat method for creating strings with given string and integer
	pattern := "OK"
	fmt.Println(strings.Repeat(pattern, 3))
}
```
```
go run utility.go

OKOKOK
```

   So in this example, we can see that the string `"OK"` is passed to the method `Repeat` with the integer `3` and thus it is appended into itself three times and the resultant string becomes `"OKOKOK"`.

#### strings.Fields

- Parameters: string
- Return Type: []string

The `Fields` method is used to extract the words from the string, that is the characters/bytes and group them with one or more consecutive white spaces. The function returns a slice of string.

```go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Fields method for extracting string from the given string with white space as delimiters
	text := "Python is a prgramming language.   Go is not"
	text_data := strings.Fields(text)
	fmt.Println(text_data)
	for _, d := range text_data {
		fmt.Println("data = ", d)
	}
}
```   

```
go run utility.go

[Python is a prgramming language. Go is not]
data =  Python
data =  is
data =  a
data =  prgramming
data =  language.
data =  Go
data =  is
data =  not
```

   The above example demonstrates the usage of `Fields` which will extract characters and split after encountering whitespace. So, we return a slice of string in which the string elements are split before white space. Thus, using the Fields method we get the words or characters as space-separated values in the slice. 

   You can even expand on this method with the [FieldsFunc](https://pkg.go.dev/strings#FieldsFunc) method which allows you to write a custom delimiter option and extract data according to your requirement. There are tons of methods in the strings package for working with strings, you can see a detailed list of functions in the [documentation](https://pkg.go.dev/strings#pkg-functions).

That's it from this part. Reference for all the code examples and commands can be found in the [100 days of Golang](https://github.com/mr-destructive/100-days-of-golang/) GitHub repository.

## Conclusion

So, from this post, we were able to understand the different methods and types to concatenate and interpolate strings in golang. We explored different types of concatenating strings, string comparison, and various methods for manipulating and interpolating strings. 

Thank you for reading, if you have any queries, feedback, or questions, you can freely ask me on my social handles. Happy Coding :)