Working with Strings, Time, Control Flow, and Functions in Go

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 prefix
  • HasSuffix(s string, suffix string) bool - Checks if string s ends with the specified suffix
  • Index(s string, substr string) int - Returns the index of the first occurrence of substr in s, or -1 if not found
  • LastIndex(s string, substr string) int - Returns the index of the last occurrence of substr in s, or -1 if not found
  • Replace(s string, old string, new string, n int) string - Replaces old with new in s, n times maximum
  • Count(s string, substr string) int - Returns the number of non-overlapping instances of substr in s
  • Repeat(s string, count int) string - Returns a new string consisting of count copies of s
  • ToLower(s string) string - Converts all characters in s to lowercase
  • ToUpper(s string) string - Converts all characters in s to uppercase
  • TrimSpace(s string) string - Removes leading and trailing whitespace characters
  • Trim(s string, cutset string) string - Removes all leading and trailing characters in cutset
  • TrimLeft(s string, cutset string) string - Removes leading characters in cutset
  • TrimRight(s string, cutset string) string - Removes trailing characters in cutset
  • Fields(s string) []string - Splits s into substrings after removing whitespace
  • Split(s string, sep string) []string - Splits s into substrings separated by sep
  • Join(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 string
  • Atoi(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

  • break terminates the innermost for, switch, or select statement
  • continue skips 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

  1. No function overloading: A package cannot contain multiple functions with the same name
  2. First-class functions: Functions can be assigned to variables and passed as arguments
  3. Anonymous functions: Functions can be defined without a name
  4. 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:

  1. Pass by value: A copy of the value is passed
  2. 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

  1. Closing file handles
  2. Releasing locks
  3. Closing database connections
  4. 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)
}

Tags: Go strings strconv time pointers

Posted on Fri, 08 May 2026 17:02:42 +0000 by waradmin