Go Line Filters

Introduction

Line filters in Go are programs that read input from standard input (stdin), process each line of input, and then write the output to standard output (stdout). They are commonly used for text processing tasks such as filtering, transforming, and analyzing text data. In this chapter, you will learn the basics of creating line filters in Go, including reading input, processing lines, and writing output.

Reading from Standard Input

To read input from stdin, you can use the bufio package, which provides a buffered reader that makes it easy to read lines of text.

Example: Reading Lines from Standard Input

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Println(line)
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program reads lines from stdin and prints them to stdout. The scanner.Err method checks for errors during reading.

Processing Lines

You can add logic to process each line as it is read. This can include filtering lines based on certain criteria, transforming the text, or performing other operations.

Example: Filtering Lines

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        if strings.Contains(line, "Go") {
            fmt.Println(line)
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program reads lines from stdin and prints only those lines that contain the substring "Go".

Example: Transforming Lines

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        upperLine := strings.ToUpper(line)
        fmt.Println(upperLine)
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program reads lines from stdin, converts each line to uppercase, and then prints it to stdout.

Writing to Standard Output

Writing to stdout can be done using the fmt package, as shown in the previous examples. For more complex output formatting or buffering, you can use the bufio package.

Example: Writing with Buffered Writer

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    writer := bufio.NewWriter(os.Stdout)
    defer writer.Flush()

    for scanner.Scan() {
        line := scanner.Text()
        upperLine := strings.ToUpper(line)
        writer.WriteString(upperLine + "\n")
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program uses a buffered writer to write each transformed line to stdout, ensuring efficient writing.

Handling Command-Line Arguments

Many line filters accept command-line arguments to control their behavior. You can use the os package to access these arguments.

Example: Filtering Lines with Command-Line Arguments

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    if len(os.Args) < 2 {
        fmt.Fprintln(os.Stderr, "Usage: go run main.go <substring>")
        os.Exit(1)
    }
    substring := os.Args[1]

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line := scanner.Text()
        if strings.Contains(line, substring) {
            fmt.Println(line)
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program takes a substring as a command-line argument and prints only those lines from stdin that contain the specified substring.

Error Handling

Proper error handling is crucial for robust programs. Always check for errors when reading from stdin and writing to stdout.

Example: Comprehensive Error Handling

Example:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    writer := bufio.NewWriter(os.Stdout)
    defer writer.Flush()

    for scanner.Scan() {
        line := scanner.Text()
        _, err := writer.WriteString(line + "\n")
        if err != nil {
            fmt.Fprintln(os.Stderr, "Error writing output:", err)
            os.Exit(1)
        }
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Error reading input:", err)
        os.Exit(1)
    }
}

In this example, the program handles errors during both reading and writing, ensuring that any issues are reported and the program exits gracefully.

Conclusion

Line filters in Go are powerful tools for processing text data. By reading from stdin, processing each line, and writing to stdout, you can create flexible and efficient text processing programs. Proper error handling and the use of command-line arguments can make your line filters more robust and user-friendly. With these techniques, you can build a wide range of text processing tools in Go.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top