Go Strings: Creation, Manipulation, Formatting
Strings are fundamental to any programming language. Go treats strings as immutable sequences of bytes, but provides powerful tools for working with them. This guide covers string creation, manipulation, formatting, and best practices.
String Basics
Creating Strings
package main
import "fmt"
func main() {
// String literal
s1 := "Hello, World!"
// Raw string literal (preserves escape sequences)
s2 := `Line 1
Line 2
Line 3`
// Empty string
s3 := ""
// String from rune
s4 := string('A')
// String from bytes
s5 := string([]byte{72, 101, 108, 108, 111})
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
fmt.Println(s5)
}
String Properties
package main
import "fmt"
func main() {
s := "Hello"
// Length in bytes
fmt.Println(len(s)) // 5
// Access byte at index
fmt.Println(s[0]) // 72 (ASCII for 'H')
fmt.Println(s[1]) // 101 (ASCII for 'e')
// Iterate over bytes
for i := 0; i < len(s); i++ {
fmt.Printf("%c ", rune(s[i]))
}
// Output: H e l l o
// Iterate over runes
for _, r := range s {
fmt.Printf("%c ", r)
}
// Output: H e l l o
}
String Manipulation
Basic Operations
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, World!"
// Concatenation
result := s + " How are you?"
fmt.Println(result)
// Substring
fmt.Println(s[0:5]) // "Hello"
fmt.Println(s[7:]) // "World!"
fmt.Println(s[:5]) // "Hello"
// Contains
fmt.Println(strings.Contains(s, "World")) // true
// Index
fmt.Println(strings.Index(s, "World")) // 7
fmt.Println(strings.LastIndex(s, "l")) // 10
// Count
fmt.Println(strings.Count(s, "l")) // 3
}
String Transformation
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, World!"
// Case conversion
fmt.Println(strings.ToLower(s)) // "hello, world!"
fmt.Println(strings.ToUpper(s)) // "HELLO, WORLD!"
fmt.Println(strings.Title(s)) // "Hello, World!"
// Trimming
s2 := " hello "
fmt.Println(strings.TrimSpace(s2)) // "hello"
fmt.Println(strings.Trim(s2, " ")) // "hello"
fmt.Println(strings.TrimPrefix(s, "Hello")) // ", World!"
fmt.Println(strings.TrimSuffix(s, "!")) // "Hello, World"
// Replace
fmt.Println(strings.Replace(s, "World", "Go", 1)) // "Hello, Go!"
fmt.Println(strings.ReplaceAll(s, "l", "L")) // "HeLLo, WorLd!"
}
Splitting and Joining
package main
import (
"fmt"
"strings"
)
func main() {
// Split
s := "apple,banana,cherry"
parts := strings.Split(s, ",")
fmt.Println(parts) // [apple banana cherry]
// Split with limit
parts2 := strings.SplitN(s, ",", 2)
fmt.Println(parts2) // [apple banana,cherry]
// Split on whitespace
s2 := "hello world go"
parts3 := strings.Fields(s2)
fmt.Println(parts3) // [hello world go]
// Join
joined := strings.Join(parts, " - ")
fmt.Println(joined) // "apple - banana - cherry"
}
String Formatting
Using fmt Package
package main
import "fmt"
func main() {
name := "Alice"
age := 30
height := 5.7
// Printf - formatted output
fmt.Printf("Name: %s, Age: %d, Height: %.1f\n", name, age, height)
// Sprintf - formatted string
result := fmt.Sprintf("Name: %s, Age: %d", name, age)
fmt.Println(result)
}
Format Verbs
package main
import "fmt"
func main() {
// String and character
fmt.Printf("%s\n", "hello") // hello
fmt.Printf("%q\n", "hello") // "hello" (quoted)
fmt.Printf("%c\n", 65) // A (character)
// Integer
fmt.Printf("%d\n", 42) // 42 (decimal)
fmt.Printf("%x\n", 255) // ff (hexadecimal)
fmt.Printf("%o\n", 8) // 10 (octal)
fmt.Printf("%b\n", 5) // 101 (binary)
// Float
fmt.Printf("%f\n", 3.14) // 3.140000
fmt.Printf("%.2f\n", 3.14159) // 3.14 (2 decimal places)
fmt.Printf("%e\n", 1000.0) // 1.000000e+03 (scientific)
fmt.Printf("%g\n", 1000.0) // 1000 (general format)
// Boolean
fmt.Printf("%v\n", true) // true
fmt.Printf("%t\n", true) // true
// General
fmt.Printf("%v\n", 42) // 42 (value)
fmt.Printf("%T\n", 42) // int (type)
fmt.Printf("%#v\n", []int{1,2}) // []int{1, 2} (Go syntax)
}
Width and Precision
package main
import "fmt"
func main() {
// Width
fmt.Printf("|%5s|\n", "hi") // | hi|
fmt.Printf("|%-5s|\n", "hi") // |hi |
fmt.Printf("|%05d|\n", 42) // |00042|
// Precision
fmt.Printf("%.2f\n", 3.14159) // 3.14
fmt.Printf("%.5s\n", "hello") // hello
// Width and precision
fmt.Printf("%10.2f\n", 3.14159) // " 3.14"
}
Runes and Bytes
Understanding Runes
package main
import "fmt"
func main() {
// Rune is an alias for int32
r := 'A'
fmt.Printf("%T\n", r) // int32
fmt.Println(r) // 65
// String to runes
s := "Hello"
runes := []rune(s)
fmt.Println(runes) // [72 101 108 108 111]
// Runes to string
s2 := string(runes)
fmt.Println(s2) // Hello
// Iterate over runes
for i, r := range s {
fmt.Printf("%d: %c (%d)\n", i, r, r)
}
}
Working with Bytes
package main
import "fmt"
func main() {
// String to bytes
s := "Hello"
bytes := []byte(s)
fmt.Println(bytes) // [72 101 108 108 111]
// Bytes to string
s2 := string(bytes)
fmt.Println(s2) // Hello
// Modify bytes
bytes[0] = 'J'
s3 := string(bytes)
fmt.Println(s3) // Jello
}
Unicode Handling
package main
import (
"fmt"
"unicode"
"unicode/utf8"
)
func main() {
s := "Hello, ไธ็"
// Length in bytes vs runes
fmt.Println(len(s)) // 13 (bytes)
fmt.Println(utf8.RuneCountInString(s)) // 9 (runes)
// Check character properties
fmt.Println(unicode.IsLetter('a')) // true
fmt.Println(unicode.IsDigit('5')) // true
fmt.Println(unicode.IsSpace(' ')) // true
// Case conversion
fmt.Println(unicode.ToLower('A')) // 97
fmt.Println(unicode.ToUpper('a')) // 65
}
String Comparison
package main
import (
"fmt"
"strings"
)
func main() {
s1 := "hello"
s2 := "hello"
s3 := "world"
// Equality
fmt.Println(s1 == s2) // true
fmt.Println(s1 == s3) // false
// Case-insensitive comparison
fmt.Println(strings.EqualFold("Hello", "hello")) // true
// Lexicographic comparison
fmt.Println(s1 < s3) // true (h < w)
fmt.Println(strings.Compare(s1, s3)) // -1 (s1 < s3)
}
String Building
Using strings.Builder
package main
import (
"fmt"
"strings"
)
func main() {
var sb strings.Builder
sb.WriteString("Hello")
sb.WriteString(", ")
sb.WriteString("World")
sb.WriteRune('!')
result := sb.String()
fmt.Println(result) // Hello, World!
}
Concatenation Performance
package main
import (
"fmt"
"strings"
)
func main() {
// โ Bad: String concatenation in loop
var result string
for i := 0; i < 1000; i++ {
result += fmt.Sprintf("Item %d\n", i)
}
// โ
Good: Using strings.Builder
var sb strings.Builder
for i := 0; i < 1000; i++ {
fmt.Fprintf(&sb, "Item %d\n", i)
}
result = sb.String()
fmt.Println(result)
}
Common String Patterns
Checking String Properties
package main
import (
"fmt"
"strings"
)
func main() {
s := "hello"
// Check if empty
if s == "" {
fmt.Println("Empty string")
}
// Check if starts/ends with
if strings.HasPrefix(s, "he") {
fmt.Println("Starts with 'he'")
}
if strings.HasSuffix(s, "lo") {
fmt.Println("Ends with 'lo'")
}
// Check if contains
if strings.Contains(s, "ll") {
fmt.Println("Contains 'll'")
}
}
String Validation
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
// Email validation (simple)
email := "[email protected]"
if strings.Contains(email, "@") && strings.Contains(email, ".") {
fmt.Println("Valid email format")
}
// Regex validation
re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
if re.MatchString(email) {
fmt.Println("Valid email")
}
}
Best Practices
โ Good Practices
- Use strings.Builder for concatenation - More efficient than
+ - Use raw strings for multi-line text - Preserves formatting
- Use fmt.Sprintf for formatting - Cleaner than concatenation
- Handle Unicode properly - Use runes for character operations
- Use strings package functions - Optimized and tested
- Validate input strings - Check for empty, length, format
- Use immutability - Strings are immutable, design accordingly
โ Anti-Patterns
// โ Bad: String concatenation in loop
var result string
for i := 0; i < 1000; i++ {
result += "Item " + strconv.Itoa(i) + "\n"
}
// โ
Good: Use strings.Builder
var sb strings.Builder
for i := 0; i < 1000; i++ {
fmt.Fprintf(&sb, "Item %d\n", i)
}
// โ Bad: Ignoring Unicode
for i := 0; i < len(s); i++ {
// Doesn't work correctly with multi-byte characters
}
// โ
Good: Use range for runes
for _, r := range s {
// Works correctly with Unicode
}
Summary
Go strings are powerful and flexible:
- Create strings with literals or conversion
- Manipulate with strings package functions
- Format with fmt package
- Handle Unicode with runes
- Use strings.Builder for efficient concatenation
- Follow best practices for performance and correctness
Master these string operations for effective Go programming.
Comments