0Pricing
Go Academy · Lesson

Unicode and utf8

Handle multibyte text.

Unicode and utf8 is a free Go Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Go Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Unicode Matters

Modern text contains accents, emoji, and non-Latin scripts. Unicode gives every character a number called a code point.

Go stores strings as UTF-8, a variable-width encoding of those code points.

package main

import "fmt"

func main() {
    s := "héllo 世界"
    fmt.Println(s)
}

Bytes per Character Vary

In UTF-8 a character uses 1 to 4 bytes:

  • ASCII letters: 1 byte.
  • Accented Latin: usually 2 bytes.
  • CJK characters: usually 3 bytes.
package main

import "fmt"

func main() {
    fmt.Println(len("a"))
    fmt.Println(len("é"))
    fmt.Println(len("世"))
}

The unicode/utf8 Package

The unicode/utf8 package understands the encoding. utf8.RuneCountInString(s) counts characters correctly, unlike len.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "héllo"
    fmt.Println(len(s))
    fmt.Println(utf8.RuneCountInString(s))
}

Validating UTF-8

Use utf8.ValidString(s) to confirm a string holds well-formed UTF-8 before processing it.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    fmt.Println(utf8.ValidString("héllo"))
    fmt.Println(utf8.ValidString(string([]byte{0xff, 0xfe})))
}

Decoding One Rune

utf8.DecodeRuneInString(s) returns the first rune and how many bytes it consumed. This lets you walk a string manually.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    r, size := utf8.DecodeRuneInString("世界")
    fmt.Println(string(r), size)
}

Range Decodes for You

A for range loop already does the UTF-8 decoding. The index jumps by the rune's byte size each step.

package main

import "fmt"

func main() {
    for i, r := range "a世b" {
        fmt.Println(i, string(r))
    }
}

The unicode Package

The unicode package classifies single runes. Functions like unicode.IsLetter and unicode.IsDigit answer questions about a character.

package main

import (
    "fmt"
    "unicode"
)

func main() {
    fmt.Println(unicode.IsLetter('A'))
    fmt.Println(unicode.IsDigit('7'))
    fmt.Println(unicode.IsSpace(' '))
}

Changing Case by Rune

unicode.ToUpper and unicode.ToLower work on individual runes, including many non-ASCII letters.

package main

import (
    "fmt"
    "unicode"
)

func main() {
    fmt.Println(string(unicode.ToUpper('a')))
    fmt.Println(string(unicode.ToLower('Z')))
}

Counting Letters Safely

Combine the packages to count only letters in mixed text, treating each rune correctly.

package main

import (
    "fmt"
    "unicode"
)

func main() {
    count := 0
    for _, r := range "a1 b2 世!" {
        if unicode.IsLetter(r) {
            count++
        }
    }
    fmt.Println(count)
}

Reversing by Runes

To reverse text safely you must reverse runes, not bytes, or multibyte characters break apart.

package main

import "fmt"

func main() {
    runes := []rune("a世b")
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    fmt.Println(string(runes))
}

Putting It Together

A quick summary tool: report byte length, rune count, and whether the text is valid UTF-8.

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "Go 世界!"
    fmt.Println("bytes:", len(s))
    fmt.Println("runes:", utf8.RuneCountInString(s))
    fmt.Println("valid:", utf8.ValidString(s))
}

Quick Check

Choose the correct way to count characters.

Recap: Unicode and utf8

You now handle multibyte text confidently:

  • Go strings are UTF-8; characters take 1 to 4 bytes.
  • unicode/utf8 counts, validates, and decodes runes.
  • unicode classifies and changes the case of single runes.
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s := "café 語"
    fmt.Println(utf8.RuneCountInString(s), "runes")
}

Frequently asked questions

Is the “Unicode and utf8” lesson free?

Yes — the full text of “Unicode and utf8” is free to read here on the web, and the Go Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Go Academy course, upgrade to CoddyKit PRO.

What will I learn in “Unicode and utf8”?

Handle multibyte text. You practise Go Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Go Academy?

No prior experience is required. Go Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Unicode and utf8” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Go Academy lesson?

Yes. Every Go Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Runes vs Bytes
  2. strings Package Functions
  3. strings.Builder
  4. Unicode and utf8
← Back to Go Academy