# Strings

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Strings and Time
Source: https://www.devtutorials.ai/learn/go/strings-and-time/strings

> 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

```go runnable title="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)
}
```

```text
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.

| Escape | Means |
|---|---|
| `\n` | new line |
| `\t` | tab (a wide space) |
| `\"` | a double quote inside the text |
| `\\` | one backslash |

```go runnable title="Escape sequences"
package main

import "fmt"

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

```text
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.

```go runnable title="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)
}
```

```text
C:\school\new\marks.txt
Roses are red,
Go is quite neat.
```

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

## String Operations

### Length

```go runnable title="len counts bytes"
package main

import "fmt"

func main() {
	fmt.Println(len("Ravi"))
	fmt.Println(len("Ravi Kumar"))
	fmt.Println(len(""))
}
```

```text
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 `+`.

```go runnable title="Join strings"
package main

import "fmt"

func main() {
	first, last := "Asha", "Rao"
	full := first + " " + last
	full += ", Class 8A"
	fmt.Println(full)
}
```

```text
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`.

```go runnable title="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:])
}
```

```text
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

```go runnable title="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"))
}
```

```text
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.

### Contains

```go runnable title="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, "#"))
}
```

```text
true
true
8
-1
```

- `Index` gives the position where the text starts.
- `-1` means "not found".

### Split and Join

```go runnable title="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, " | "))
}
```

```text
[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

```go runnable title="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"))
}
```

```text
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

```go runnable title="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***", "*"))
}
```

```text
"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.

```go runnable title="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]))
}
```

```text
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**.

```go runnable title="range gives runes"
package main

import "fmt"

func main() {
	for i, r := range "Go₹" {
		fmt.Println(i, string(r))
	}
}
```

```text
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.

- `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.

```go runnable title="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())
}
```

```text
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

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `s[0] = 'X'` | Strings can be changed in place | Strings are immutable | A build error. Make a new string. |
| `len("नमस्ते")` for the letter count | `len` counts characters | `len` counts bytes | You get 18, not 6. |
| `s[0]` to get the first letter of Hindi text | Each index is a character | Each index is a byte | A broken character. Use `[]rune(s)`. |
| `"Roll " + 7` | `+` joins any value | Convert numbers first | `mismatched types` |
| `strings.Replace(s, a, b)` | It replaces all by default | It needs a count. Use `ReplaceAll` | A build error (missing argument). |
| `strings.ToUpper(s)` without using the result | It changes `s` | It returns a new string | `s` stays the same. |
| `+=` in a big loop | It is free | Each `+` copies the string | Slow 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.
