So, I want to make these lines of code can return the value as first in first out, I was trying to do that but it's only read the last input for the first output
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
for {
scanner.Scan()
input := scanner.Text()
if input == "" {
break
}
defer fmt.Println(input)
}
}
$ go run main.go
1
2
3
3
2
1
Make use of string slice
func main() {
var (
scanner = bufio.NewScanner(os.Stdin)
output []string
)
for {
scanner.Scan()
input := scanner.Text()
if input == "" {
break
}
output = append(output, input)
}
for _, val := range output {
fmt.Println(val)
}
}
Related
I am new to the language GO and working on an assignment where i should write a code that return the word frequencies of the text. However I know that the words 'Hello', 'HELLO' and 'hello' are all counted as 'hello', so I need to convert all strings to lower case.
I know that I should use strings.ToLower(), however I dont know where I should Included that in the class. Can someone please help me?
package main
import (
"fmt"
"io/ioutil"
"log"
"strings"
"time"
)
const DataFile = "loremipsum.txt"
// Return the word frequencies of the text argument.
func WordCount(text string) map[string]int {
fregs := make(map[string]int)
words := strings.Fields(text)
for _, word := range words {
fregs[word] += 1
}
return fregs
}
// Benchmark how long it takes to count word frequencies in text numRuns times.
//
// Return the total time elapsed.
func benchmark(text string, numRuns int) int64 {
start := time.Now()
for i := 0; i < numRuns; i++ {
WordCount(text)
}
runtimeMillis := time.Since(start).Nanoseconds() / 1e6
return runtimeMillis
}
// Print the results of a benchmark
func printResults(runtimeMillis int64, numRuns int) {
fmt.Printf("amount of runs: %d\n", numRuns)
fmt.Printf("total time: %d ms\n", runtimeMillis)
average := float64(runtimeMillis) / float64(numRuns)
fmt.Printf("average time/run: %.2f ms\n", average)
}
func main() {
// read in DataFile as a string called data
data, err:= ioutil.ReadFile("loremipsum.txt")
if err != nil {
log.Fatal(err)
}
// Convert []byte to string and print to screen
text := string(data)
fmt.Println(text)
fmt.Printf("%#v",WordCount(string(data)))
numRuns := 100
runtimeMillis := benchmark(string(data), numRuns)
printResults(runtimeMillis, numRuns)
}
You should convert words to lowercase when you are using them as map key
for _, word := range words {
fregs[strings.ToLower(word)] += 1
}
I get [a:822 a.:110 I want all a in the same. How do i a change the code so that a and a. is the same? – hello123
You need to carefully define a word. For example, a string of consecutive letters and numbers converted to lowercase.
func WordCount(s string) map[string]int {
wordFunc := func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
}
counts := make(map[string]int)
for _, word := range strings.FieldsFunc(s, wordFunc) {
counts[strings.ToLower(word)]++
}
return counts
}
to remove all non-word characters you could use a regular expression:
package main
import (
"bufio"
"fmt"
"log"
"regexp"
"strings"
)
func main() {
str1 := "This is some text! I want to count each word. Is it cool?"
re, err := regexp.Compile(`[^\w]`)
if err != nil {
log.Fatal(err)
}
str1 = re.ReplaceAllString(str1, " ")
scanner := bufio.NewScanner(strings.NewReader(str1))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(strings.ToLower(scanner.Text()))
}
}
See strings.EqualFold.
Here is an example.
This question already has an answer here:
Golang, a proper way to rewind file pointer
(1 answer)
Closed 1 year ago.
I have a program which generates a random number between 1 and the number of lines in a file - call that number n. It then reads the file until iterator i == n and then prints that line from the file.
I'm seeing very strange behaviour though which I'm struggling to explain. For some reason, when I allow my code to seed and generate a random number, the bufio scanner fails and does not print the name from the file. When I comment the number generation part out, the exact same code works and a name is printed as expected.
In the below code, commenting and un-commenting the code between the hashtags inexplicably changes the behaviour of the bufio scanner code - and by that I mean it either prints or doesn't print a name from the file.
The file I refer to is just a list of names e.g.
name1
name2
name3
NOTE:
go version == 1.16.3 darwin/amd64
Code:
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
f, err := os.Open(nameFile)
if err != nil {
log.Fatalln(err)
}
defer f.Close()
// ############## RANDOM NUMBER GENERATION ##############
// min := 1
// max, err := lineCounter(f)
// if err != nil {
// log.Fatalln(err)
// }
// rand.Seed(time.Now().UnixNano())
// v := rand.Intn(max-min) + min
// fmt.Println(v)
// ############## RANDOM NUMBER GENERATION ##############
scanner := bufio.NewScanner(f)
i := 0
for scanner.Scan() {
i += 1
if i == 60 {
fmt.Println(scanner.Text())
}
}
if err := scanner.Err(); err != nil {
log.Fatalln(err)
}
}
func lineCounter(r io.Reader) (int, error) {
buf := make([]byte, 32*1024)
count := 0
lineSep := []byte{'\n'}
for {
c, err := r.Read(buf)
count += bytes.Count(buf[:c], lineSep)
switch {
case err == io.EOF:
return count, nil
case err != nil:
return count, err
}
}
}
Your problem can be simplified way, way down:
package main
import (
"bufio"
"io"
"strings"
)
func main() {
f := strings.NewReader(strings.Repeat("north\n", 9))
io.ReadAll(f)
s := bufio.NewScanner(f)
for s.Scan() {
println(s.Text())
}
}
So as you can see, it's nothing to do with the random numbers, and it's not even anything to do with a file. When you are calling that "bad function", you're reading up all the data in the reader, so nothing is left for the Scanner to use.
How can I read a file in Go and skip the first line / headers?
In Python I know I could do
counter = 0
with open("my_file_path", "r") as fo:
try:
next(fo)
except:
pass
for _ in fo:
counter = counter + 1
This is my Go application
package main
import (
"bufio"
"flag"
"os"
)
func readFile(fileLocation string) int {
fileOpen, _ := os.Open(fileLocation)
defer fileOpen.Close()
fileScanner := bufio.NewScanner(fileOpen)
counter := 0
for fileScanner.Scan() {
//fmt.Println(fileScanner.Text())
counter = counter + 1
}
return counter
}
func main() {
fileLocation := flag.String("file_location", "default value", "file path to count lines")
flag.Parse()
counted := readFile(*fileLocation)
println(counted)
}
I will be reading a huge file and don't want to be evaluating each line if the index is 0.
How about to move to the next token before the loop
scanner := bufio.NewScanner(file)
scanner.Scan() // this moves to the next token
for scanner.Scan() {
fmt.Println(scanner.Text())
}
file
1
2
3
output
2
3
https://play.golang.org/p/I2w50zFdcg0
For example,
package main
import (
"bufio"
"fmt"
"os"
)
func readFile(filename string) (int, error) {
f, err := os.Open(filename)
if err != nil {
return 0, err
}
defer f.Close()
count := 0
s := bufio.NewScanner(f)
if s.Scan() {
for s.Scan() {
count++
}
}
if err := s.Err(); err != nil {
return 0, err
}
return count, nil
}
func main() {
filename := `test.file`
count, err := readFile(filename)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(count)
}
Output:
$ cat test.file
1234567890
abc
$ go run count.go
1
$
you can try something like this
func readFile(fileLocation string) int {
fileOpen, _ := os.Open(fileLocation)
defer fileOpen.Close()
fileScanner := bufio.NewScanner(fileOpen)
counter := 0
for fileScanner.Scan() {
// read first line and ignore
fileScanner.Text()
break
}
for fileScanner.Scan() {
// read remaining lines and process
txt := fileScanner.Text()
counter = counter + 1
// do something with text
}
return counter
}
Edit:
func readFile(fileLocation string) int {
fileOpen, _ := os.Open(fileLocation)
defer fileOpen.Close()
fileScanner := bufio.NewScanner(fileOpen)
counter := 0
if fileScanner.Scan() {
// read first line and ignore
fileScanner.Text()
}
for fileScanner.Scan() {
// read remaining lines and process
txt := fileScanner.Text()
// do something with text
counter = counter + 1
}
return counter
}
I'm trying to write a Go script that takes in as many lines of comma-separated coordinates as the user wishes, split and convert the string of coordinates to float64, store each line as a slice, and then append each slice in a slice of slices for later usage.
Example inputs are:
1.1,2.2,3.3
3.14,0,5.16
Example outputs are:
[[1.1 2.2 3.3],[3.14 0 5.16]]
The equivalent in Python is
def get_input():
print("Please enter comma separated coordinates:")
lines = []
while True:
line = input()
if line:
line = [float(x) for x in line.replace(" ", "").split(",")]
lines.append(line)
else:
break
return lines
But what I wrote in Go seems way too long (pasted below), and I'm creating a lot of variables without the ability to change variable type as in Python. Since I literally just started writing Golang to learn it, I fear my script is long as I'm trying to convert Python thinking into Go. Therefore, I would like to ask for some advice as to how to write this script shorter and more concise in Go style? Thank you.
package main
import (
"fmt"
"os"
"bufio"
"strings"
"strconv"
)
func main() {
inputs := get_input()
fmt.Println(inputs)
}
func get_input() [][]float64 {
fmt.Println("Please enter comma separated coordinates: ")
var inputs [][]float64
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if len(scanner.Text()) > 0 {
raw_input := strings.Replace(scanner.Text(), " ", "", -1)
input := strings.Split(raw_input, ",")
converted_input := str2float(input)
inputs = append(inputs, converted_input)
} else {
break
}
}
return inputs
}
func str2float(records []string) []float64 {
var float_slice []float64
for _, v := range records {
if s, err := strconv.ParseFloat(v, 64); err == nil {
float_slice = append(float_slice, s)
}
}
return float_slice
}
Using only string functions:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
var result [][]float64
var txt string
for scanner.Scan() {
txt = scanner.Text()
if len(txt) > 0 {
values := strings.Split(txt, ",")
var row []float64
for _, v := range values {
fl, err := strconv.ParseFloat(strings.Trim(v, " "), 64)
if err != nil {
panic(fmt.Sprintf("Incorrect value for float64 '%v'", v))
}
row = append(row, fl)
}
result = append(result, row)
}
}
fmt.Printf("Result: %v\n", result)
}
Run:
$ printf "1.1,2.2,3.3
3.14,0,5.16
2,45,76.0, 45 , 69" | go run experiment2.go
Result: [[1.1 2.2 3.3] [3.14 0 5.16] [2 45 76 45 69]]
With given input, you can concatenate them to make a JSON string and then unmarshal (deserialize) that:
func main() {
var lines []string
for {
var line string
fmt.Scanln(&line)
if line == "" {
break
}
lines = append(lines, "["+line+"]")
}
all := "[" + strings.Join(lines, ",") + "]"
inputs := [][]float64{}
if err := json.Unmarshal([]byte(all), &inputs); err != nil {
fmt.Println(err)
return
}
fmt.Println(inputs)
}
Here's an issue that's bedeviling me at the moment. When getting input from the user, I want to employ a loop to ask the user to retry until they enter valid input:
// user_input.go
package main
import (
"fmt"
)
func main() {
fmt.Println("Please enter an integer: ")
var userI int
for {
_, err := fmt.Scanf("%d", &userI)
if err == nil {
break
}
fmt.Println("Sorry, invalid input. Please enter an integer: ")
}
fmt.Println(userI)
}
Running the above, if the user enters valid input, no problem:
Please enter an integer:
3
3
exit code 0, process exited normally.
But try inputting a string instead?
Please enter an integer:
what?
Sorry, invalid input. Please enter an integer:
Sorry, invalid input. Please enter an integer:
Sorry...
Etc, and it keeps looping character by character until the string is exhausted.
Even inputting a single character loops twice, I assume as it parses the newline.
Anyways, there must be a way to flush Stdin in Go?
P.S. In the absence of such a feature, how would you work around it to provide equivalent functionality? I've failed even at that...
I would fix this by reading until the end of the line after each failure. This clears the rest of the text.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
fmt.Println("Please enter an integer: ")
var userI int
for {
_, err := fmt.Fscan(stdin, &userI)
if err == nil {
break
}
stdin.ReadString('\n')
fmt.Println("Sorry, invalid input. Please enter an integer: ")
}
fmt.Println(userI)
}
Is it bad to wake up an old question?
I prefer to use fmt.Scanln because A) it doesn't require importing another library (e.g. reader) and B) it doesn't involve an explicit for loop.
func someFunc() {
fmt.Printf("Please enter an integer: ")
// Read in an integer
var i int
_, err := fmt.Scanln(&i)
if err != nil {
fmt.Printf("Error: %s", err.Error())
// If int read fails, read as string and forget
var discard string
fmt.Scanln(&discard)
return
}
fmt.Printf("Input contained %d", i)
}
However, it seems like there ought to be a more elegant solution. Particularly in the case of fmt.Scanln it seems odd that the read stops after the first non-number byte rather than "scanning the line".
I ran into a similar problem for getting user input but solved it in a slightly different way. Adding to the thread in case someone else finds this useful:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// Get first word from stdin
func getFirstWord() (string) {
input := bufio.NewScanner(os.Stdin)
input.Scan()
ans := strings.Fields(input.Text())
if len(ans) == 0 {
return ""
} else {
return ans[0]
}
}
func main() {
fmt.Printf("Would you like to play a game?\n> ")
ans := getFirstWord()
fmt.Printf("Your answer: %s\n", ans)
}
I know this has already been answered but this was my implementation:
func flush (reader *bufio.Reader) {
var i int
for i = 0; i < reader.Buffered(); i++ {
reader.ReadByte()
}
}
This should work in every situation, including ones where "stdin.ReadString('\n')" cannot be used.
Sorry for digging this back up, but I ran into this today and wanted to improve on the existing answers by using new standard library functionality.
import (
"bufio"
"fmt"
"os"
)
func discardBuffer(r *bufio.Reader) {
r.Discard(r.Buffered())
}
stdin := bufio.NewReader(os.Stdin)
var i int
for true {
if _, err := fmt.Fscanln(stdin, &i); err != nil {
discardBuffer(stdin)
// Handle error, display message, etc.
continue
}
// Do your other value checks and validations
break
}
The basic idea is to always buffer your reads from stdin. When you encounter an error while scanning, just discard the buffer contents. That way you start with an empty buffer for your next scan.
Alternatively, you can discard the buffer before you scan, so any stray inputs by the user before then won't get picked up.
func fscanln(r *bufio.Reader, a ...interface{}) error {
r.Discard(r.Buffered())
_, err := fmt.Fscanln(r, a...)
return err
}
stdin := bufio.NewReader(os.Stdin)
var i int
if err := fscanln(stdin, &i); err != nil {
// Handle error
}
I use this snippet to filter unnecessary leading space/new line
in := bufio.NewReader(os.Stdin)
result, err = in.ReadString('\n')
for len(strings.TrimSpace(result)) == 0 {
result, err = in.ReadString('\n')
}
I usually use bufio.Scanner since the fmt.Scan funcs always split on whitespace.
func promptYN(msg string) bool {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", msg)
s.Scan()
input := strings.ToLower(s.Text())
if input == "y" || input == "n" {
return input == "y"
}
fmt.Println("Error: expected Y or N.")
}
}
func promptInt(msg string) int {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s [int]: ", msg)
s.Scan()
output, err := strconv.Atoi(s.Text())
if err == nil {
return output
}
fmt.Println("Error: expected an integer.")
}
}
Or you could make something more universal:
func prompt(msg string, check func(string) bool) {
s := bufio.NewScanner(os.Stdin)
for {
fmt.Printf("%s: ", msg)
s.Scan()
if check(s.Text()) {
return
}
}
}
Example:
var f float64
prompt("Enter a float", func(s string) bool {
f, err = strconv.ParseFloat(s, 64)
return err == nil
})