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.
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.
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
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
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"
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
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
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
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.
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]
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
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
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
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
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
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
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
.
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.
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.
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
.
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
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
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
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.