Strings in Go

By Syncfusion®, Inc. · Last updated

Create interpreted and raw strings, find the length, join, slice and compare text, use the strings package, understand runes, Unicode and UTF-8, and build text with strings.Builder.

What Are Strings?

String = a piece of text, like a name, a sentence or a roll number.

  • In Go, a string is written in double quotes: "Asha".
  • Inside, a string is a row of bytes.
  • A string cannot be changed once it is made. You make a new string instead.

Immutable = cannot be changed after it is made.

Simple lesson: a string is text, stored as bytes, and it never changes in place.

A Real-World Example

The school office prints ID cards.

  • The clerk joins first and last names: "Asha" + " " + "Rao".
  • She trims extra spaces someone typed by mistake.
  • She makes the name CAPITAL for the card.
  • She checks if the email contains @.

Every one of these jobs is a string operation. Go's strings package has a ready-made function for each.

You met the string type in Data Types.

This lesson shows how to work with strings: join, compare, search, split, change and build them.

What You Will Learn

In this lesson, you will learn:

  1. How to create strings, and the two kinds of string literals.
  2. How to find the length, join and compare strings.
  3. The most useful functions in the strings package.
  4. How runes, Unicode and UTF-8 work, and why len counts bytes.
  5. How to build long strings fast with strings.Builder.

String Fundamentals

Creating Strings

Ways to make a string

package main

import "fmt"

func main() {
	var school string = "Green Valley School"
	city := "Pune"
	var empty string
	fmt.Println(school, city)
	fmt.Printf("%q\n", empty)
}
Green Valley School Pune
""
  • The zero value of a string is "", the empty string.

Interpreted Strings

Interpreted string = text in double quotes " ". Go understands escape sequences inside it.

Escape sequence = a backslash \ plus a letter, for a special character.

EscapeMeans
\nnew line
\ttab (a wide space)
\"a double quote inside the text
\\one backslash

Escape sequences

package main

import "fmt"

func main() {
	fmt.Println("Name:\tAsha\nClass:\t8A")
	fmt.Println("She said \"Good morning!\"")
}
Name:	Asha
Class:	8A
She said "Good morning!"

Raw Strings

Raw string = text in back-quotes ` `. Go keeps every character exactly as typed.

  • No escape sequences: \n stays as two characters.
  • It can go over many lines.
  • Great for file paths, poems and blocks of text.

A raw string

package main

import "fmt"

func main() {
	path := `C:\school\new\marks.txt`
	poem := `Roses are red,
Go is quite neat.`
	fmt.Println(path)
	fmt.Println(poem)
}
C:\school\new\marks.txt
Roses are red,
Go is quite neat.

Interpreted vs raw

Simple lesson: use " " for normal text with escapes, and back-quotes for text kept exactly as typed.

String Operations

Length

len counts bytes

package main

import "fmt"

func main() {
	fmt.Println(len("Ravi"))
	fmt.Println(len("Ravi Kumar"))
	fmt.Println(len(""))
}
4
10
0
  • len() counts bytes, and a space is a byte too.
  • For English letters, one letter is one byte. See Runes and Unicode for other languages.

Concatenation

Concatenation = joining strings end to end with +.

Join strings

package main

import "fmt"

func main() {
	first, last := "Asha", "Rao"
	full := first + " " + last
	full += ", Class 8A"
	fmt.Println(full)
}
Asha Rao, Class 8A
  • += adds text to the end. It actually makes a new string.
  • To join a number, convert it first: "Roll " + strconv.Itoa(7). See Data Types.

Indexing and Slicing

  • s[i] gives the byte at position i, starting at 0.
  • s[a:b] gives the text from position a up to, but not including, b.

Pick parts of a string

package main

import "fmt"

func main() {
	code := "GVS-2025-8A"
	fmt.Println(code[0], string(code[0]))
	fmt.Println(code[:3])
	fmt.Println(code[4:8])
	fmt.Println(code[9:])
}
71 G
GVS
2025
8A
  • code[0] is a byte, so it prints as the number 71.
  • code[:3] means "from the start to 3". code[9:] means "from 9 to the end".
  • You cannot change a byte: code[0] = 'X' does not build.

Comparison

Compare strings

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println("Asha" == "Asha")
	fmt.Println("Asha" == "asha")
	fmt.Println("apple" < "banana")
	fmt.Println(strings.EqualFold("GO", "go"))
}
true
false
true
true
  • == and != check for the exact same text. Capital letters matter.
  • < and > compare in dictionary order, byte by byte.
  • strings.EqualFold compares and ignores capital letters.

Simple lesson: len counts bytes, + joins, [a:b] cuts out a part, and == compares exactly.

String Functions

The strings package has many ready-made functions. Click each tab to see one group.

The strings package, by job

Contains

Search in text

package main

import (
	"fmt"
	"strings"
)

func main() {
	email := "asha.rao@gvs.edu"
	fmt.Println(strings.Contains(email, "@"))
	fmt.Println(strings.HasSuffix(email, ".edu"))
	fmt.Println(strings.Index(email, "@"))
	fmt.Println(strings.Index(email, "#"))
}
true
true
8
-1
  • Index gives the position where the text starts.
  • -1 means "not found".

Split and Join

Split a CSV line, then join it back

package main

import (
	"fmt"
	"strings"
)

func main() {
	line := "Asha,8A,91"
	parts := strings.Split(line, ",")
	fmt.Println(parts, len(parts))
	fmt.Println(parts[0], "is in", parts[1])
	fmt.Println(strings.Join(parts, " | "))
}
[Asha 8A 91] 3
Asha is in 8A
Asha | 8A | 91
  • Split gives a slice of strings. You will learn slices in Slices.
  • Join is the opposite of Split.

Replace

Replace text

package main

import (
	"fmt"
	"strings"
)

func main() {
	notice := "Holiday on Monday. Monday classes move to Saturday."
	fmt.Println(strings.Replace(notice, "Monday", "Friday", 1))
	fmt.Println(strings.ReplaceAll(notice, "Monday", "Friday"))
}
Holiday on Friday. Monday classes move to Saturday.
Holiday on Friday. Friday classes move to Saturday.
  • The last number in Replace is how many to change. -1 means all.
  • ReplaceAll changes every match.

Trim, ToUpper and ToLower

Clean up a name for an ID card

package main

import (
	"fmt"
	"strings"
)

func main() {
	typed := "   kavya sharma \n"
	clean := strings.TrimSpace(typed)
	fmt.Printf("%q\n", clean)
	fmt.Println(strings.ToUpper(clean))
	fmt.Println(strings.ToLower("GVS"))
	fmt.Println(strings.Trim("***TOPPER***", "*"))
}
"kavya sharma"
KAVYA SHARMA
gvs
TOPPER
  • TrimSpace removes spaces, tabs and new lines at both ends only.
  • The spaces in the middle stay.

Simple lesson: the strings package can search, split, join, replace, trim and change case, always returning a new string.

Runes and Unicode

Unicode Characters

Unicode = one big list that gives every character in every language a number.

  • A is 65.
  • अ is 2309.
  • 😀 is 128512.

Rune = one Unicode character in Go. It is an int32. See Data Types.

UTF-8

UTF-8 = the way Go stores Unicode characters as bytes.

  • English letters take 1 byte.
  • Hindi, Tamil and most Indian scripts take 3 bytes per character.
  • Emoji take 4 bytes.

Type some text and watch the bytes.

Compare the two numbers:

  • len counts bytes.
  • RuneCountInString counts characters.

Bytes vs characters

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	word := "नमस्ते"
	fmt.Println(len(word))
	fmt.Println(utf8.RuneCountInString(word))
	letters := []rune(word)
	fmt.Println(string(letters[0]))
}
18
6
न
  • []rune(word) turns the text into a list of characters.
  • Then letters[0] is the first character, not the first byte.

range Over a String

range walks through a string one rune at a time.

range gives runes

package main

import "fmt"

func main() {
	for i, r := range "Go₹" {
		fmt.Println(i, string(r))
	}
}
0 G
1 o
2 ₹
  • ₹ uses 3 bytes, so if there were a next character, it would start at position 5.
  • range handles that for you.

s[i] gives a byte, not a character.

For text that may not be English, use range or []rune(s).

Simple lesson: Go stores text as UTF-8 bytes, so use runes when you mean characters.

String Builder

strings.Builder

Every + makes a brand-new string. In a big loop, that wastes time and memory.

strings.Builder = a tool that collects text piece by piece, and makes the final string once.

Press Play to watch a builder fill up.

Build a roll list

  • var b strings.Builder is ready to use. No set-up needed.
  • b.WriteString(s) adds text.
  • fmt.Fprintf(&b, ...) adds formatted text. The & is explained in Pointers.
  • b.String() gives the result.

A report line with a Builder

package main

import (
	"fmt"
	"strings"
)

func main() {
	marks := []int{91, 78, 85}
	var b strings.Builder
	b.WriteString("Marks: ")
	for i, m := range marks {
		if i > 0 {
			b.WriteString(", ")
		}
		fmt.Fprintf(&b, "%d", m)
	}
	fmt.Println(b.String())
}
Marks: 91, 78, 85

Easy way to remember

  • A few joins: + is fine.
  • A loop of joins: use strings.Builder.
  • A list to join: strings.Join.

Simple lesson: use strings.Builder to build a long string piece by piece.

TL;DR

  1. String = immutable text, stored as UTF-8 bytes.

    • Interpreted = "...", with \n, \t, \".
    • Raw = back-quotes, kept exactly, can span lines.
  2. Length = len(s), in bytes. Join = +. Part = s[a:b].
  3. Compare = ==, <, or strings.EqualFold to ignore case.
  4. strings package = Contains, Index, Split, Join, Replace, ReplaceAll, Trim, TrimSpace, ToUpper, ToLower, Repeat, Fields.
  5. Rune = one character. UTF-8 = 1 to 4 bytes per character.

    • utf8.RuneCountInString(s) = number of characters.
    • range over a string gives runes.
  6. strings.Builder = build long text fast: WriteString, then String().

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
s[0] = 'X'Strings can be changed in placeStrings are immutableA build error. Make a new string.
len("नमस्ते") for the letter countlen counts characterslen counts bytesYou get 18, not 6.
s[0] to get the first letter of Hindi textEach index is a characterEach index is a byteA broken character. Use []rune(s).
"Roll " + 7+ joins any valueConvert numbers firstmismatched types
strings.Replace(s, a, b)It replaces all by defaultIt needs a count. Use ReplaceAllA build error (missing argument).
strings.ToUpper(s) without using the resultIt changes sIt returns a new strings stays the same.
+= in a big loopIt is freeEach + copies the stringSlow programs. Use strings.Builder.

Check Yourself

Exercises

What to Remember

  • A string is immutable text, stored as UTF-8 bytes.
  • "..." understands \n and \t. Back-quotes keep text exactly.
  • len counts bytes. Use utf8.RuneCountInString or []rune for characters.
  • strings functions return a new string. Store the result.
  • Split and Join turn text into a list and back.
  • Use strings.Builder to build long text in a loop.

Next: Time and Date. You will learn how to work with dates, times and durations in Go.