String Manipulation with strings and strconv Packages
The strings package provides various functions for string manipulation:
HasPrefix(s string, prefix string) bool- Checks if string s starts with the specified prefixHasSuffix(s string, suffix string) bool- Checks if string s ends with the specified suffixIndex(s string, substr string) int- Returns the index of the first occurrence of substr in s, or -1 if not foundLastIndex(s string, substr string) int- Returns the index of the last occurrence of substr in s, or -1 if not foundReplace(s string, old string, new string, n int) string- Replaces old with new in s, n times maximumCount(s string, substr string) int- Returns the number of non-overlapping instances of substr in sRepeat(s string, count int) string- Returns a new string consisting of count copies of sToLower(s string) string- Converts all characters in s to lowercaseToUpper(s string) string- Converts all characters in s to uppercaseTrimSpace(s string) string- Removes leading and trailing whitespace charactersTrim(s string, cutset string) string- Removes all leading and trailing characters in cutsetTrimLeft(s string, cutset string) string- Removes leading characters in cutsetTrimRight(s string, cutset string) string- Removes trailing characters in cutsetFields(s string) []string- Splits s into substrings after removing whitespaceSplit(s string, sep string) []string- Splits s into substrings separated by sepJoin(elems []string, sep string) string- Joins elements of elems with sep between
Practical Example: URL and Path Processing
package main
import (
"fmt"
"strings"
)
func formatURL(url string) string {
if !strings.HasPrefix(url, "http://") {
url = "http://" + url
}
return url
}
func ensureTrailingSlash(path string) string {
if !strings.HasSuffix(path, "/") {
path += "/"
}
return path
}
func main() {
var url, path string
fmt.Scanln(&url, &path)
url = formatURL(url)
path = ensureTrailingSlash(path)
fmt.Println(url)
fmt.Println(path)
}
strconv Package for Type Conversion
The strconv package handles conversions between strings and other data types:
Itoa(i int) string- Converts integer i to a stringAtoi(s string) (int, error)- Converts string s to an integer
Practical Example: String Manipulation and Conversion
package main
import (
"fmt"
"strconv"
"strings"
)
func demonstrateStringOps(input string) {
// Replace first occurrence
replaced := strings.Replace(input, "world", "universe", 1)
fmt.Println("After replacement:", replaced)
// Count specific characters
charCount := strings.Count(input, "l")
fmt.Println("Character count:", charCount)
// Repeat string
repeated := strings.Repeat(input, 2)
fmt.Println("Repeated string:", repeated)
// Case conversions
lower := strings.ToLower(input)
upper := strings.ToUpper(input)
fmt.Println("Lowercase:", lower)
fmt.Println("Uppercase:", upper)
// Trim operations
trimmed := strings.TrimSpace(input)
fmt.Println("Trimmed:", trimmed)
// Split and join
fields := strings.Fields(input)
fmt.Println("Split by spaces:", fields)
joined := strings.Join(fields, "-")
fmt.Println("Joined with hyphen:", joined)
// String to number conversion
numStr := strconv.Itoa(42)
fmt.Println("Integer to string:", numStr)
if num, err := strconv.Atoi("123"); err == nil {
fmt.Println("String to integer:", num)
} else {
fmt.Println("Conversion error:", err)
}
}
func main() {
input := " Hello World Example "
demonstrateStringOps(input)
}
Date and Time Handling in Go
The time package provides functionality for measuring and displaying time:
package main
import (
"fmt"
"time"
)
func formatCurrentTime() {
now := time.Now()
formatted := now.Format("2006/01/02 15:04:05")
fmt.Println("Current time:", formatted)
}
func measureExecutionTime() {
start := time.Now().UnixNano()
// Simulate some work
time.Sleep(100 * time.Millisecond)
end := time.Now().UnixNano()
duration := (end - start) / 1000 // Convert to microseconds
fmt.Printf("Execution time: %d microseconds\n", duration)
}
func main() {
formatCurrentTime()
measureExecutionTime()
}
Time Durations
The time.Duration type represents the elapsed time between two instants as nanoseconds:
const (
Nanosecond time.Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
Pointer Types in Go
In Go, there are two types of variables:
- Value types: Variables store the actual value
- Reference types: Variables store a memory address where the actual value is stored
Working with Pointers
To get the address of a variable, use the & operator. To access the value at an address, use the * operator.
package main
import "fmt"
func demonstratePointers() {
// Create an integer variable
num := 42
fmt.Println("Original value:", num)
// Get the address of num
ptr := &num
fmt.Println("Memory address:", ptr)
// Access the value through the pointer
fmt.Println("Value via pointer:", *ptr)
// Modify the value through the pointer
*ptr = 100
fmt.Println("Modified value:", num)
}
func modifyValue(ptr *int) {
*ptr = 200
}
func main() {
demonstratePointers()
original := 50
fmt.Println("Before function call:", original)
modifyValue(&original)
fmt.Println("After function call:", original)
}
Control Flow in Go
If-Else Statements
Go supports conditional execution with if-else statements:
package main
import "fmt"
func checkNumber(n int) {
if n > 0 {
fmt.Println("Positive number")
} else if n < 0 {
fmt.Println("Negative number")
} else {
fmt.Println("Zero")
}
}
func main() {
checkNumber(10)
checkNumber(-5)
checkNumber(0)
}
Switch Statements
Switch statements in Go are more flexible than in many other languages:
package main
import "fmt"
func evaluateGrade(score int) {
switch {
case score >= 90:
fmt.Println("Grade: A")
case score >= 80:
fmt.Println("Grade: B")
case score >= 70:
fmt.Println("Grade: C")
case score >= 60:
fmt.Println("Grade: D")
default:
fmt.Println("Grade: F")
}
}
func demonstrateFallthrough() {
switch value := 5; value {
case 5:
fmt.Println("Value is 5")
fallthrough
case 10:
fmt.Println("Fallthrough to this case")
default:
fmt.Println("Default case")
}
}
func main() {
evaluateGrade(85)
evaluateGrade(72)
evaluateGrade(59)
demonstrateFallthrough()
}
For Loops
Go has only one looping construct: the for loop. It can be used in several ways:
package main
import "fmt"
func basicForLoop() {
// Standard for loop
for i := 0; i < 5; i++ {
fmt.Println("Iteration:", i)
}
}
func conditionalForLoop() {
// While-like for loop
count := 0
for count < 5 {
fmt.Println("Count:", count)
count++
}
}
func infiniteLoop() {
// Infinite loop with break
for {
fmt.Println("This will loop forever")
break
}
}
func nestedLoops() {
// Nested loops with continue
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
continue
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
}
func forRangeLoop() {
// Using for range with a string
text := "Hello"
for index, char := range text {
fmt.Printf("Index: %d, Character: %c\n", index, char)
}
}
func main() {
basicForLoop()
conditionalForLoop()
infiniteLoop()
nestedLoops()
forRangeLoop()
}
Goto and Labels
While generally discouraged, Go does support goto statements with labels:
package main
import "fmt"
func demonstrateGoto() {
i := 0
START:
fmt.Println("Current value:", i)
i++
if i < 5 {
goto START
}
}
func demonstrateContinueWithLabel() {
outerLoop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
continue outerLoop
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
}
func main() {
demonstrateGoto()
demonstrateContinueWithLabel()
}
Break and Continue
breakterminates the innermost for, switch, or select statementcontinueskips the rest of the current iteration and moves to the next one
Functions in Go
Function Declaration
Functions in Go are declared with the func keyword:
package main
import "fmt"
// No parameters, no return values
func greet() {
fmt.Println("Hello, World!")
}
// Two parameters, no return values
func add(a, b int) {
fmt.Println("Sum:", a+b)
}
// Two parameters, one return value
func multiply(a, b int) int {
return a * b
}
// Two parameters, multiple return values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
// Named return values
func subtract(a, b int) (result int) {
result = a - b
return
}
func main() {
greet()
add(5, 3)
product := multiply(4, 6)
fmt.Println("Product:", product)
quotient, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Quotient:", quotient)
}
difference := subtract(8, 3)
fmt.Println("Difference:", difference)
}
Function Characteristics in Go
- No function overloading: A package cannot contain multiple functions with the same name
- First-class functions: Functions can be assigned to variables and passed as arguments
- Anonymous functions: Functions can be defined without a name
- Multiple return values: Functions can return multiple values
Function Types and Assignment
package main
import "fmt"
// Define a function type
type mathFunc func(int, int) int
// Regular function
func calculateSum(a, b int) int {
return a + b
}
// Function that takes another function as parameter
func applyOperation(op mathFunc, x, y int) int {
return op(x, y)
}
// Anonymous function
func demonstrateAnonymousFunctions() {
// Assign anonymous function to variable
multiply := func(a, b int) int {
return a * b
}
fmt.Println("Multiply:", multiply(3, 4))
// Immediately invoked function
result := (func(a, b int) int {
return a - b
})(10, 4)
fmt.Println("Subtract:", result)
}
func main() {
// Assign function to variable
sumFunc := calculateSum
fmt.Println("Sum:", sumFunc(5, 3))
// Pass function as argument
result := applyOperation(calculateSum, 7, 8)
fmt.Println("Applied operation:", result)
demonstrateAnonymousFunctions()
}
Parameter Passing
Go uses two ways to pass parameters to functions:
- Pass by value: A copy of the value is passed
- Pass by reference: A copy of the address is passed (more efficient for large values)
Named Return Values
Functions can have named return values that can be referenced within the function:
package main
import "fmt"
// Function with named return values
func calculateStats(numbers []int) (min, max, avg float64) {
if len(numbers) == 0 {
return 0, 0, 0
}
min = float64(numbers[0])
max = float64(numbers[0])
sum := 0
for _, num := range numbers {
if float64(num) < min {
min = float64(num)
}
if float64(num) > max {
max = float64(num)
}
sum += num
}
avg = float64(sum) / float64(len(numbers))
return
}
func main() {
nums := []int{5, 10, 15, 20, 25}
min, max, avg := calculateStats(nums)
fmt.Printf("Min: %.2f, Max: %.2f, Average: %.2f\n", min, max, avg)
}
Ignoring Return Values
The blank identifier _ can be used to ignore return values:
func main() {
// Only use the first return value, ignore the error
result, _ := someFunctionReturningTwoValues()
fmt.Println("Result:", result)
}
Variadic Functions
Variadic functions can accept a variable number of arguments:
package main
import "fmt"
// Function with multiple arguments of the same type
func sumAll(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
// Function with at least one parameter and multiple additional parameters
func concatenate(base string, parts ...string) string {
result := base
for _, part := range parts {
result += part
}
return result
}
func main() {
// Call with variable arguments
total := sumAll(1, 2, 3, 4, 5)
fmt.Println("Sum:", total)
// Call with slice using spread syntax
nums := []int{6, 7, 8, 9}
total = sumAll(nums...)
fmt.Println("Sum with slice:", total)
// Concatenate strings
text := concatenate("Hello", " ", "World", "!")
fmt.Println("Concatenated:", text)
}
Defer Statements
The defer statement schedules a function call to be executed when the surrounding function returns:
package main
import "fmt"
func fileOperations() {
// Simulate opening a file
fmt.Println("Opening file...")
// Schedule file closing
defer fmt.Println("Closing file...")
// Simulate file operations
fmt.Println("Reading data...")
fmt.Println("Processing data...")
// This will execute before the function returns
fmt.Println("File operations complete")
}
func demonstrateMultipleDefers() {
defer fmt.Println("First defer")
defer fmt.Println("Second defer")
defer fmt.Println("Third defer")
fmt.Println("Function body")
}
func demonstrateDeferWithVariables() {
a := 1
defer fmt.Println("Deferred value:", a)
a = 2
fmt.Println("Current value:", a)
}
func main() {
fileOperations()
fmt.Println("\nMultiple defers:")
demonstrateMultipleDefers()
fmt.Println("\nDefer with variables:")
demonstrateDeferWithVariables()
}
Common Uses of Defer
- Closing file handles
- Releasing locks
- Closing database connections
- Releasing other resources
Practical Examples
Example 1: Multiplication Table
package main
import "fmt"
func printMultiplicationTable(size int) {
for i := 1; i <= size; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d×%d=%-4d", j, i, i*j)
}
fmt.Println()
}
}
func main() {
printMultiplicationTable(9)
}
Example 2: Finding Perfect Numbers
A perfect number is a positive integer that is equal to the sum of its proper divisors:
package main
import "fmt"
// Check if a number is perfect
func isPerfectNumber(n int) bool {
if n <= 1 {
return false
}
sum := 1 // 1 is a proper divisor for all numbers > 1
for i := 2; i*i <= n; i++ {
if n%i == 0 {
sum += i
if i != n/i {
sum += n/i
}
}
}
return sum == n
}
// Find all perfect numbers up to n
func findPerfectNumbers(limit int) []int {
var perfects []int
for i := 2; i <= limit; i++ {
if isPerfectNumber(i) {
perfects = append(perfects, i)
}
}
return perfects
}
func main() {
limit := 10000
perfects := findPerfectNumbers(limit)
fmt.Printf("Perfect numbers up to %d: %v\n", limit, perfects)
}
Example 3: Palindrome Checker
package main
import "fmt"
// Check if a string is a palindrome
func isPalindrome(s string) bool {
// Convert string to rune array to handle Unicode characters
runes := []rune(s)
length := len(runes)
// Compare characters from start and end moving toward center
for i := 0; i < length/2; i++ {
if runes[i] != runes[length-i-1] {
return false
}
}
return true
}
func main() {
testStrings := []string{"radar", "hello", "level", "world", "A man, a plan, a canal: Panama"}
for _, str := range testStrings {
if isPalindrome(str) {
fmt.Printf("\"%s\" is a palindrome\n", str)
} else {
fmt.Printf("\"%s\" is not a palindrome\n", str)
}
}
}
Example 4: Character Counter
package main
import (
"bufio"
"fmt"
"os"
"unicode"
)
// Count different types of characters in a string
func countCharacters(input string) (letters, spaces, digits, others int) {
for _, char := range input {
switch {
case unicode.IsLetter(char):
letters++
case unicode.IsSpace(char):
spaces++
case unicode.IsDigit(char):
digits++
default:
others++
}
}
return
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter a string: ")
input, _ := reader.ReadString('\n')
letters, spaces, digits, others := countCharacters(input)
fmt.Printf("Letters: %d\n", letters)
fmt.Printf("Spaces: %d\n", spaces)
fmt.Printf("Digits: %d\n", digits)
fmt.Printf("Others: %d\n", others)
}
Example 5: Large Number Addition
Adding numbers that exceed the maximum integer range:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// Add two large numbers represented as strings
func addLargeNumbers(num1, num2 string) string {
// Handle empty strings
if num1 == "" && num2 == "" {
return "0"
}
// Reverse both strings to start from least significant digit
reversed1 := reverseString(num1)
reversed2 := reverseString(num2)
// Find the maximum length
maxLen := len(reversed1)
if len(reversed2) > maxLen {
maxLen = len(reversed2)
}
// Pad with zeros if necessary
for len(reversed1) < maxLen {
reversed1 += "0"
}
for len(reversed2) < maxLen {
reversed2 += "0"
}
// Perform addition digit by digit
result := make([]byte, maxLen)
carry := 0
for i := 0; i < maxLen; i++ {
digit1 := int(reversed1[i] - '0')
digit2 := int(reversed2[i] - '0')
sum := digit1 + digit2 + carry
carry = sum / 10
result[i] = byte(sum%10) + '0'
}
// Add remaining carry if any
if carry > 0 {
result = append(result, byte(carry)+'0')
}
// Reverse result and return
return reverseString(string(result))
}
// Helper function to reverse a string
func reverseString(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter two numbers separated by +: ")
input, _ := reader.ReadString('\n')
parts := strings.Split(strings.TrimSpace(input), "+")
if len(parts) != 2 {
fmt.Println("Invalid input format. Please use: number1+number2")
return
}
num1 := strings.TrimSpace(parts[0])
num2 := strings.TrimSpace(parts[1])
result := addLargeNumbers(num1, num2)
fmt.Printf("Result: %s\n", result)
}