Learning Center
Go

Introduction to Strings in Go

6 Dec 2024
Hostman Team
Hostman Team

A string in Go is a basic data type that represents a simple sequence of bytes but comes with special methods for working with them.

Golang provides the built-in strings package, which contains essential (and quite simple) functions for handling string data. These functions are similar to typical string functions in other programming languages like C or C++.

In the examples in this article, we also use the fmt package to format and print strings to the console.

Apart from defining strings, Go offers an extensive set of capabilities for performing various string manipulations.

Declaring Strings
Copy link

It is worth mentioning that strings in Golang are somewhat different from those in Java, C++, or Python. A string in Go is a sequence of characters where each character can vary in size, which means it can be represented by one or more bytes in UTF-8 encoding.

Historically, when the C language was developed, a character in a computer was represented by a 7-bit ASCII code. Thus, a string was essentially a collection of multiple 7-bit ASCII characters.

However, as the use of computers grew globally, the 7-bit ASCII scheme became insufficient for supporting characters from different languages. This led to the development of various character encoding models like Unicode, UTF-8, UTF-16, UTF-32, etc.

Different programming languages adopted their own character encoding schemes. For example, Java originally used UTF-16. On the other hand, Go is built on UTF-8 encoding.

Thanks to UTF-8, Golang strings can contain universal text, representing a mix of any existing language in the world — without confusion or limitations. Additionally, strings in Go are immutable, meaning you cannot change their content after they are created.

Declaring Strings with Double Quotes
Copy link

There are several common ways to declare (define) strings in Go:

// Explicit declaration using "var"
var variable1 = "some text"

// Explicit declaration using "var" with a type specified
var variable2 string = "peace"

// Shorter declaration
variable3 := "some text"

You can also declare a string without an explicit value. In this case, the string variable is initialized with a zero value, which is an empty string:

var variable4 string

When double quotes are used to create a string variable, Golang interprets special (escaped) characters, written with a backslash (\). For example, \n represents a new line:

import "fmt"

...

var some_variable = "first line\nsecond line\nthird line"

fmt.Println(some_variable) // Print the string to the console

// OUTPUT:
// first line
// second line
// third line

Declaring Strings with Backticks
Copy link

To make the Go compiler ignore special characters and preserve the original formatting of strings, you can use backticks (`).

Here’s an example of declaring a Go string with explicit formatting:

import "fmt"

...

// Line breaks in the variable are specified explicitly without adding special characters
var some_variable = `first line
second line
third line`

fmt.Println(some_variable) // Print the string to the console

// OUTPUT:
// first line
// second line
// third line

Notice that the fmt package is used to output strings to the console.

In this example, Golang completely ignores escaped characters:

import "fmt"

...

var some_variable = `upper line \n lower line`

fmt.Println(some_variable) // Print the string to the console

// OUTPUT: upper line \n lower line

Modifying Strings in Go
Copy link

The purpose of the special String type is to allow working with more than just a "raw" sequence of bytes; it provides dedicated methods for managing strings.

However, strictly speaking, strings are immutable in Go (unlike C and C++) — they cannot be changed. You can only access individual characters by their index:

import "fmt"

...

variable := "hello"
c := variable[0]

fmt.Printf("%c\n", c) // OUTPUT: h

Despite this, there are many ways to create new strings from existing ones.

Some functions for string manipulation require the strings package:

import "strings"

String Concatenation in Golang
Copy link

The most basic string manipulation is concatenating multiple strings into one. This is done using the + operator:

import "fmt"

...

var variable1 = "hello"
var variable2 = "world"
var space = " "

var variable3 = variable1 + space + variable2

fmt.Println(variable3) // OUTPUT: hello world
fmt.Println(variable2 + ", " + variable1) // OUTPUT: world, hello

Note: You cannot add strings to other types, such as numbers:

fmt.Println("I am " + 24 + " years old") // ERROR

To make the above example work, you need to convert the number to a string using a type conversion function, such as strconv.Itoa:

import "fmt"
import "strconv"

...

age := 24

fmt.Println("I am " + strconv.Itoa(age) + " years old") // OUTPUT: I am 24 years old
fmt.Println("I am " + strconv.Itoa(24) + " years old") // OUTPUT: I am 24 years old

Trimming Strings
Copy link

You can trim specific characters from the beginning and end of a string by specifying them as an argument:

import (
  "fmt"
  "strings"
)

...

result := strings.Trim("xxxhello worldxxx", "xxx")
fmt.Println(result) // OUTPUT: hello world

Splitting Strings
Copy link

You can split a string into substrings by specifying a delimiter:

import (
  "fmt"
  "strings"
)

...

result := strings.Split("hello world", " ")
fmt.Println(result) // OUTPUT: [hello world

Joining Strings
Copy link

You can join multiple Go strings stored in an array into a single string by explicitly specifying a delimiter:

import (
  "fmt"
  "strings"
)

...

result := strings.Join([]string{"hello", "world"}, " ") // an array of strings is provided as an argument
fmt.Println(result) // OUTPUT: hello world

However, using a join function or the + operator for string concatenation is not always efficient. Each such operation creates a new string, which can reduce performance.

To address this, Go provides an optimized tool for constructing strings from components while following specific rules — the Builder:

import (
  "fmt"
  "strings"
)

...

builded := &strings.Builder{}

builded.WriteString("very")
builded.WriteString(" ")
builded.WriteString("long")
builded.WriteString(" ")
builded.WriteString("line")

fmt.Println(builded.String()) // OUTPUT: very long line

For more details, refer to the official Golang documentation on the Builder type. Despite its powerful optimization capabilities, it is straightforward to use, as it doesn’t have a large number of methods.

Splitting Strings
Copy link

You can split a string into parts by specifying a delimiter as an argument:

import (
  "fmt"
  "strings"
)

...

result := strings.Split("h-e-l-l-o", "-")

fmt.Println(result) // OUTPUT: [h e l l o]

Replacing Substrings
Copy link

Go provides several ways to replace a substring with another:

import (
  "fmt"
  "strings"
)

...

result := strings.Replace("hello", "l", "|", 1) // replace the first occurrence
fmt.Println(result) // OUTPUT: he|lo

result = strings.Replace("hello", "l", "|", -1) // replace all occurrences
fmt.Println(result) // OUTPUT: he||o

Changing String Case
Copy link

Go also provides methods to switch the case of a string — converting to uppercase or lowercase:

import (
  "fmt"
  "strings"
)

...

fmt.Println(strings.ToUpper("hello")) // OUTPUT: HELLO
fmt.Println(strings.ToLower("HELLO")) // OUTPUT: hello

Creating a String from a Sequence of Bytes
Copy link

You can also convert a sequence of bytes into a full-fledged string and then work with it:

import "fmt"

...

// byte sequence
any_bytes := []byte{0x47, 0x65, 0x65, 0x6b, 0x73}

// create a string
any_string := string(any_bytes)

fmt.Println(any_string) // OUTPUT: Geeks

Comparing Strings
Copy link

Searching for a Substring
Copy link

One way to check if a substring is present in a string is by using the strings.Contains function:

import (
  "fmt"
  "strings"
)

...

result := strings.Contains("world", "rl")
fmt.Println(result) // OUTPUT: true

result = strings.Contains("world", "rrl")
fmt.Println(result) // OUTPUT: false

Classic Comparison Operators
Copy link

You can also use standard comparison operators to check for matches. These operators compare strings character by character in lexicographical order and consider the length of the strings:

import "fmt"

...

fmt.Println("hello" == "hello")       // OUTPUT: true
fmt.Println("hello" == "hello world") // OUTPUT: false
fmt.Println("hello" > "hell")         // OUTPUT: true
fmt.Println("hello" > "lo")           // OUTPUT: false

Checking for Prefixes and Suffixes
Copy link

In addition to searching for substrings, you can check if a string contains a specific prefix or suffix using strings.HasPrefix and strings.HasSuffix:

import (
  "fmt"
  "strings"
)

...

result := strings.HasPrefix("hello", "he")
fmt.Println(result) // OUTPUT: true

result = strings.HasSuffix("hello", "lo")
fmt.Println(result) // OUTPUT: true

result = strings.HasPrefix("hello", "el")
fmt.Println(result) // OUTPUT: false

Finding the Index of a Substring
Copy link

You can obtain the index of the first occurrence of a specified substring using the strings.Index function:

import (
  "fmt"
  "strings"
)

...

result := strings.Index("hello", "el")
fmt.Println(result) // OUTPUT: 1

result = strings.Index("hello", "le")
fmt.Println(result) // OUTPUT: -1

If the substring is not found, the function returns -1.

String Length
Copy link

To determine the length of a string in Golang, you can use the built-in len function:

import "fmt"

...

length := len("hello")

fmt.Println(length) // OUTPUT: 5

Since Go uses UTF-8 encoding, the length of a string corresponds to the number of bytes, not the number of characters, as some characters may occupy 2 or more bytes.

Iterating Over a String
Copy link

In some cases, such as comparing strings or processing their content, you may need to iterate through a string's characters manually.  This can be achieved using a for loop with a range clause:

import "fmt"

...

for symbol_index, symbol_value := range "Hello For All Worlds" {
  fmt.Printf("Value: %c; Index: %d\n", symbol_value, symbol_index)
  // additional actions can be performed here
}

This loop retrieves both the index and the value of each character in the string, making it easy to process each symbol individually.

String Output and Formatting
Copy link

Formatting with Basic Types
Copy link

The fmt package in Go offers powerful tools for formatting strings during their output. Similar to other programming languages, Golang uses templates and annotation verbs for formatting.

Here are some examples:

import "fmt"

...

// Formatting a string variable using %s
any_string := "hello"
result := fmt.Sprintf("%s world", any_string)

fmt.Println(result) // OUTPUT: hello world

// Formatting a number variable using %d
any_number := 13
result = fmt.Sprintf("there are %d worlds!", any_number)

fmt.Println(result) // OUTPUT: there are 13 worlds!

// Formatting a boolean variable using %t
any_boolean := true
result = fmt.Sprintf("this is the %t world!", any_boolean)

fmt.Println(result) // OUTPUT: this is the true world!

The Sprintf function formats and returns the string, which can then be printed to the console using Println.

Using Multiple Variables in Formatting
Copy link

You can use more complex templates to include multiple variables in the same format string:

import "fmt"

...

// Formatting two strings in one template
first_string := "hello"
second_string := "world"
result := fmt.Sprintf("%s %s", first_string, second_string)

fmt.Println(result) // OUTPUT: hello world

// Formatting three numbers in one template
first_number := 10
second_number := 20
third_number := 30
result = fmt.Sprintf("%d and %d and %d", first_number, second_number, third_number)

fmt.Println(result) // OUTPUT: 10 and 20 and 30

// Formatting two boolean values in one template
first_boolean := true
second_boolean := false
result = fmt.Sprintf("if it is not %t therefore it means it is %t", first_boolean, second_boolean)

fmt.Println(result) // OUTPUT: if it is not true therefore it means it is false

Mixing Different Variable Types
Copy link

You can combine variables of different types within a single formatted string:

import "fmt"

...

first_string := "hello"
second_number := 13
third_boolean := true

result := fmt.Sprintf("%s to all %d %t worlds", first_string, second_number, third_boolean)

fmt.Println(result) // OUTPUT: hello to all 13 true worlds

Formatting with Binary Data
Copy link

Go allows for special formatting of numbers into binary representation using %b:

import "fmt"

...

first_number := 13
second_number := 25
result := fmt.Sprintf("%b and %b", first_number, second_number)

fmt.Println(result) // OUTPUT: 1101 and 11001

Conclusion
Copy link

Go provides a small yet sufficient toolkit for string manipulation, covering most of a developer's needs.

One important concept to understand when working with Golang strings is that what we conventionally call "individual elements of a string" (characters) are actually sequences of UTF-8 bytes. This means that when working with strings, we are manipulating byte values.

As a result, any attempt (which is prohibited in Go) to modify a two-byte character into a single byte would result in an error.

Each time we "modify" a string, what we are actually doing is recreating it with updated values.

Similarly, when we query the length of a string, we are retrieving the number of bytes used, not the number of characters.

Nevertheless, Go's standard libraries are rich with functions for "manipulating" strings. This introductory article has demonstrated basic yet commonly used methods for interacting with strings in Golang.

Keep in mind that in most cases, advanced string usage requires importing the specialized strings package and leveraging the fmt package to format strings for console output.

For a complete and detailed reference of all available methods in the strings package, you can consult the official Go documentation. In addition,  you can deploy Go applications (such as Beego and Gin) on our app platform.