Golang read multiple lines from stdin [SOLVED]

Golang read multiple lines from stdin [SOLVED]

Different methods to read multiple lines from STDIN

In this article, I will demonstrate multiline input in a Go terminal application. In the previous article, we discussed about how to parse multiple inputs from users in Golang. Now, we are going to introduce 3 methods to read multiple lines from the console:

  • fmt.Scan()
  • bufio.Reader()
  • bufio.Scanner()

Method-1: Use <a href=“https://pkg.go.dev/fmt#Scan" target="_blank”

rel=“noopener”>fmt.Scan()to read multiple lines of text

func Scan(a ...any) (n int, err error): Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. It returns the number of items successfully scanned.

In the example below, we use the Scan() function to read 3 lines from stdin and save each line as a variable.

package main

import (
    "fmt"
    "log"
)

func main() {
    fmt.Println("input text:")
    var line1, line2, line3 string
    _, err := fmt.Scan(&line1, &line2, &line3)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("3 lines of text: %s %s %s\n", line1, line2, line3)
}

Output:

input text:
line1
line2
line3
3 lines of text: line1 line2 line3

Note that the Scan() function considers newlines as spaces, so each line cannot contain space characters.

Method-2: Use <a href=“https://pkg.go.dev/bufio#Reader" target="_blank”

rel=“noopener”>bufio.Readerto read multiple lines of text

func (b *Reader) ReadString(delim byte) (string, error): ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF).

We will show you an example of using ReadString() to input multiple lines of text until the line only contains space characters.

package main

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

func main() {
    fmt.Println("input text:")
    reader := bufio.NewReader(os.Stdin)

    var lines []string
    for {
        // read line from stdin using newline as separator
        line, err := reader.ReadString('\n')
        if err != nil {
            log.Fatal(err)
        }

        // if line is empty, break the loop
        if len(strings.TrimSpace(line)) == 0 {
            break
        }

        //append the line to a slice
        lines = append(lines, line)
    }

    //print out all the lines
    println(len(lines))
    fmt.Println("output:")
    for _, eachLine := range lines {
        fmt.Println(eachLine)
    }
}

Output:

input text:
This is GoLinuxCloud
Welcome to our website

output:
This is GoLinuxCloud

Welcome to our website

Note that the ReadString() function returns a string containing the data up to and including the delimiter, so all the lines will contain ‘\n’ at the end of line.

Method-3: Use <a href=“https://pkg.go.dev/bufio#Scanner" target="_blank”

rel=“noopener”>bufio.Scanner to read multiple lines of text until the empty line

func (s *Scanner) Text() string: Text returns the most recent token generated by a call to Scan as a newly allocated string holding its bytes.

Here is an example of using Scanner() to input multiple lines of text until the input line is empty.

package main

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

func main() {
    fmt.Println("input text:")
    scanner := bufio.NewScanner(os.Stdin)

    var lines []string
    for {
        scanner.Scan()
        line := scanner.Text()

        // break the loop if line is empty
        if len(line) == 0 {
            break
        }
        lines = append(lines, line)
    }

    err := scanner.Err()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("output:")
    for _, eachLine := range lines {
        fmt.Println(eachLine)
    }
}

Output:

input text:
Today is Sunday
Have a nice weekend!

output:
Today is Sunday
Have a nice weekend!

Read multiple lines of text from STDIN conditionally

We can modify the code from Section 3 to read lines from the console and add a condition to read until it reaches a line that only contains the ‘Q’ character.

package main

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

func main() {
    fmt.Println("input text:")
    scanner := bufio.NewScanner(os.Stdin)

    var lines []string
    for {
        scanner.Scan()
        line := scanner.Text()

        // break the loop if line is contains 'q' only
        if strings.ToLower(strings.TrimSpace(line)) == "q" {
            break
        }
        lines = append(lines, line)
    }

    err := scanner.Err()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("output:")
    for _, eachLine := range lines {
        fmt.Println(eachLine)
    }
}

Output:

input text:
Hello
This is our website

Q
output:
Hello
This is our website

Summary

There are some ways to input multiple lines from console in Golang

  • Using the fmt.Scan() in case reading multiple words where each word is on a separate line.
  • Using the bufio.Readerin case reading multiple lines together with the newline character at the end of each line.
  • Using bufio.Scanner, which allows us to get a list of input lines without the newline character at the end of each line, is the most recommended and universal method of reading multiple lines.

References

https://pkg.go.dev/bufio#Scanner.Scan
https://pkg.go.dev/fmt#Scan

Tuan Nguyen

Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise spanning these technologies, he develops robust solutions and implements efficient data processing and management strategies across various projects and platforms.