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.
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.
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 method in the strings package 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
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 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.
// 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 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.
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. 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.
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, WriteByte, and 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() method to format it as a string.
If we look at the bytes.Buffer 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:
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.
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:
Using Compare method
We also have the Compare 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 for further clarity.
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 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
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, ToUpper, and 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
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 method in the strings package has been deprecated due to incompatibility with certain languages and cases. So, we are using the text/cases package to get the 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 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 object will have certain methods and properties attached to it, we will use the method Strings 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 and 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
.
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, IndexAny, LastIndex, etc. you can find the list of methods in the strings package.
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 and SplitAfter methods are quite handy to know about.
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 and 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.
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.
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 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.
That's it from this part. Reference for all the code examples and commands can be found in the 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 :)