I have the following program that I want to show how many times a specific introduced line appears from stdin:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
counts[input.Text()]++
}
// NOTE: ignoring potential errors from input.Err()
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s
", n, line)
}
}
}
When I run the program it allows me to enter strings, but even when I press enter I don't get any feedback.
What am I missing here? I believe it gets stuked in the first for
.
Try this - save the following text in /tmp/input.txt or other local file:
Line A
Line B
Line B
Line A
Line C
Line B
Now pipe the contents of that file as standard input to your program - for example in /tmp/q.go: cat /tmp/input.txt | go run /tmp/q.go
The output should be:
$ cat /tmp/input.txt | go run /tmp/q.go
2 Line A
3 Line B
your code first gets all lines, then prints the result at the end (EOF
).
1 - if you need feedback for each line when you press Enter: edit your first loop:
for input.Scan() {
txt := input.Text()
counts[txt]++
fmt.Println("counts[", txt, "] =", counts[txt])
}
like this working sample code:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
txt := input.Text()
counts[txt]++
fmt.Println("counts[", txt, "] =", counts[txt])
}
// NOTE: ignoring potential errors from input.Err()
for line, n := range counts {
if n > 1 {
fmt.Printf("%d\t%s
", n, line)
}
}
}
2- if you want the result at the end of data entry, you have two options:
Option A: press EOF
at the end of lines in terminal (command prompt): Ctrl+Z then Enter in Windows, or Ctrl+D in Linux.
Option B: save all text data in one file like "lines.txt" and then run your go binary file with this file as input (Redirecting input) like this:
Windows:
main.exe < lines.txt
Linux:
./main < lines.txt
The place where you are going wrong is:
You are not giving any condition for the first for loop to terminate. This leaves you with the two options suggested by Volker. Either way, you are essentially providing the program with an EOF, for it to know when to stop scanning for input.
Now, the chances are this might not look very clean to you. In that case (and only in that case), you can proceed by introducing a termination condition inside the input for loop:
if input.Text() == "" {
break;
}
This leads to the execution to reach the second for loop as soon as you enter two new-line characters. Quite handy if you would like to prompt the user on the beginning of the program to Hit enter twice to show results, or something of that sorts.
Cheers!