Fast method to take input separated by space [Golang] - go

I'm trying to take fast input through scanner.Bytes and I'm it converting to int. The problem is : input provided is single line spaced elements ( like an array ) and scanner.Bytes reads it as a whole and converts it into int. How can I use a Splitfunction on the bytes read!
func main() {
scanner := bufio.NewScanner(os.Stdin)
var t int
fmt.Scanf("%d", &t)
for o := 0; o < t; o++ {
scanner.Scan()
//scanner.Split(bufio.ScanWords())
val := toInt(scanner.Bytes())
fmt.Println(val)
}
}
func toInt(buf []byte) (n int) {
for _, v := range buf {
n = n*10 + int(v-'0')
}
return
}

The default bufio.Scanner (with the default split function) breaks the input into lines with line termination stripped. If you want to break the input into words, you have to set a different split function using the Scanner.Split() method.
There is a ready split function for breaking words: bufio.ScanWords(). We can see this function commented in your code.
But know that Scanner.Split() method expects a function value. You do not need to call the split function, the scanner will call it to break the input data. You just have to pass the function value, which you can do so without calling it, without appending () after the function name, e.g.:
scanner.Split(bufio.ScanWords)
Also note that you only have to do this once, and you can only do this once, before starting to scan the input, so move this line above the for loop:
var t int
fmt.Scanf("%d", &t)
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
for o := 0; o < t; o++ {
scanner.Scan()
val := toInt(scanner.Bytes())
fmt.Println(val)
}
Now running the app, and entering the following lines:
3
1 2 3
The output will be:
1
2
3
Also note that Scanner.Scan() returns a bool value indicating if scanning was successful and you may process the token. You should use this return value and not rely on the user inputting valid data.

why not just try something like
func main() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
val, err := strconv.Atoi(scanner.Text())
if err != nil {
log.Fatal(err)
}
fmt.Println(val)
}
}

Assuming the input is 3 65 65 65 and 3 is the number of following bytes, then this works:
func main() {
var t int
fmt.Scanf("%d", &t)
values := make([]byte, t)
buf := make([]interface{}, t)
for i := 0; i < t; i++ {
buf[i] = &values[i]
}
fmt.Scanln(buf...)
// test
fmt.Printf("%v\n", values)
}
The test output is [65 65 65].

Related

How to read inputs recursively in golang

In the following code after one recursion the inputs are not read(from stdin). Output is incorrect if N is greater than 1.
X is read as 0 after one recursive call and hence the array is not read after that.
Program is supposed to print sum of squares of positive numbers in the array. P.S has to done only using recursion
package main
// Imports
import (
"fmt"
"bufio"
"os"
"strings"
"strconv"
)
// Global Variables
var N int = 0;
var X int = 0;
var err error;
var out int = 0;
var T string = "0"; // All set to 0 just in case there is no input, so we don't crash with nil values.
func main() {
// Let's grab our input.
fmt.Print("Enter N: ")
fmt.Scanln(&N)
// Make our own recursion.
loop()
}
func loop() {
if N == 0 {return}
// Grab our array length.
fmt.Scanln(&X)
tNum := make([]string, X)
// Grab our values and put them into an array.
in := bufio.NewReader(os.Stdin)
T, err = in.ReadString('\n')
tNum = strings.Fields(T)
// Parse the numbers, square, and add.
add(tNum)
// Output and reset.
fmt.Print(out)
out = 0;
N--
loop()
}
// Another loop, until X is 0.
func add(tNum []string) {
if X == 0 {return}
// Parse a string to an integer.
i, err := strconv.Atoi(tNum[X-1])
if err != nil {}
// If a number is negative, make it 0, so when we add its' square, it does nothing.
if (i < 0) {
i = 0;
}
// Add to our total!
out = out + i*i
X--
add(tNum)
}
Input:
2
4
2 4 6 8
3
1 3 9
Output:
1200
Expected output:
120
91
bufio.Reader, like the name suggests, use a buffer to store what is in the reader (os.Stdin here), which means, each time you create a bufio.Reader and read it once, there are more than what is read stored into the buffer, and thus the next time you read from the reader (os.Stdin), you do not read from where you left.
You should only have one bufio.Reader for os.Stdin. Make it global (if that is a requirement) or make it an argument. In fact, bufio package has a Scanner type that can splits spaces and new lines so you don't need to call strings.Fields.
I think you should practise doing this yourself, but here is a playground link: https://play.golang.org/p/7zBDYwqWEZ0
Here is an example that illustrates the general principles.
// Print the sum of the squares of positive numbers in the input.
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
func sumOfSquares(sum int, s *bufio.Scanner, err error) (int, *bufio.Scanner, error) {
if err != nil {
return sum, s, err
}
if !s.Scan() {
err = s.Err()
if err == nil {
err = io.EOF
}
return sum, s, err
}
for _, f := range strings.Fields(s.Text()) {
i, err := strconv.Atoi(f)
if err != nil || i <= 0 {
continue
}
sum += i * i
}
return sumOfSquares(sum, s, nil)
}
func main() {
sum := 0
s := bufio.NewScanner(os.Stdin)
sum, s, err := sumOfSquares(sum, s, nil)
if err != nil && err != io.EOF {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(sum)
}
Input:
2
4
2 4 6 8
3
1 3 9
Output:
240

for range vs static channel length golang

I have a channel taking events parsed from a log file and another one which is used for synchronization. There were 8 events for the purpose of my test.
When using the for range syntax, I get 4 events. When using the known number (8), I can get all of them.
func TestParserManyOpinit(t *testing.T) {
ch := make(chan event.Event, 1000)
done := make(chan bool)
go parser.Parse("./test_data/many_opinit", ch, done)
count := 0
exp := 8
evtList := []event.Event{}
<-done
close(ch)
//This gets all the events
for i := 0; i < 8; i++ {
evtList = append(evtList, <-ch)
count++
}
//This only gives me four
//for range ch {
// evtList = append(evtList, <-ch)
// count++
//}
if count != exp || count != len(evtList) {
t.Errorf("Not proper lenght, got %d, exp %d, evtList %d", count, exp, len(evtList))
}
func Parse(filePath string, evtChan chan event.Event, done chan bool) {
log.Info(fmt.Sprintf("(thread) Parsing file %s", filePath))
file, err := os.Open(filePath)
defer file.Close()
if err != nil {
log.Error("Cannot read file " + filePath)
}
count := 0
scan := bufio.NewScanner(file)
scan.Split(splitFunc)
scan.Scan() //Skip log file header
for scan.Scan() {
text := scan.Text()
text = strings.Trim(text, "\n")
splitEvt := strings.Split(text, "\n")
// Some parsing ...
count++
evtChan <- evt
}
fmt.Println("Done ", count) // gives 8
done <- true
}
I must be missing something related to for loops on a channel.
I've tried adding a time.Sleep just before the done <- true part. It didn't change the result.
When you use for range, each loop iteration reads from the channel, and you're not using the read value. Hence, half the values are discarded. It should be:
for ev := range ch {
evtList = append(evtList, ev)
count++
}
In order to actually utilize the values read in the loop iterator.
Ranging over channels is demonstrated in the Tour of Go and detailed in the Go spec.

golang scan a line of numbers from sdin

I'm trying to read input from stdin like
3 2 1<ENTER>
and save it in a list of ints. At the moment my code looks like this:
nums = make([]int, 0)
var i int
for {
_, err := fmt.Scan(&i)
if err != nil {
if err==io.EOF { break }
log.Fatal(err)
}
nums = append(nums, i)
}
at the moment the program never leaves the for-loop. I can't find an easy way to check for a newline character in the documentation. how would i do this?
Edit:
Since I know that there will almost certainly be four numbers, I tried the following:
var i0,i1,i2,i3 int
fmt.Scanf("%d %d %d %d\n", &i0, &i1, &i2, &i3)
but this only scanned the first number and then exited the program. I'm not sure if that's because of the z-shell I'm using.
Edit:
To clarify, the program will pause and ask for the user to input a list of n numbers separated by spaces and terminated with a newline. these numbers should be stored in an array.
Ok, I decided to bring out the large bufio hammer and solve it like this:
in := bufio.NewReader(os.Stdin)
line, err := in.ReadString('\n')
if err != nil {
log.Fatal(err)
}
strs := strings.Split(line[0:len(line)-1], " ")
nums := make([]int, len(strs))
for i, str := range strs {
if nums[i], err = strconv.Atoi(str); err != nil {
log.Fatal(err)
}
}
It does seem like an awful lot of code, but it works.
It seems that you want https://golang.org/pkg/fmt/#Fscanln
Something like
ok := func(err error) { if err != nil { panic(err) } }
for {
var i, j, k int
_, err := fmt.Fscanln(io.Stdin, &i, &j, &k)
ok(err)
fmt.Println(i, j, k)
}
I will suggest to use "bufio" package with the "scan()" method.
Following is the code where I'm reading two lines from "stdin" and storing the lines into an array.
Hope this helps you.
package main
import (
"fmt"
"bufio"
"os"
"strconv"
"strings"
)
func ReadInput() []string{
var lines []string
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
lines = append(lines, scanner.Text())
//count, _ := strconv.Atoi(lines[0])
if len(lines) == 2 { break }
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
return lines
}
func main(){
lines := ReadInput()
count ,_ := strconv.Atoi(lines[0])
num := strings.Fields(lines[1])
if count != len(num) { os.Exit(0) }
// Do whatever you want here
}
Two lines will be accepted. First line will have a count. Second line will have all the numbers. You can modify the same code as per your requirement.
Example:
3
1 5 10

Invalid indirect of type func (int) string

I'm getting stucked with the following error:
./main.go:76: invalid indirect of Fizzbuzz (type func(int) string)
I understand that the Fizzbuzz function does not satisfy the writeString. My intuition is telling me that this is probably because I should be using an interface to Fizzbuzz? Can someone please give me some direction on how to execute this? What can I do to make this code Go idiomatic?
// -------------------------------INPUT--------------------------------------
// Your program should read an input file (provided on the command line),
// which contains multiple newline separated lines.
// Each line will contain 3 numbers which are space delimited.
// The first number is first number to divide by ('A' in this example),
// the second number is the second number to divide by ('B' in this example)
// and the third number is where you should count till ('N' in this example).
// You may assume that the input file is formatted correctly and the
// numbers are valid positive integers. E.g.
// 3 5 10
// 2 7 15
// -------------------------------OUTPUT------------------------------------
// Print out the series 1 through N replacing numbers divisible by 'A' by F,
// numbers divisible by 'B' by B and numbers divisible by both as 'FB'.
// Since the input file contains multiple sets of values, your output will
// print out one line per set. Ensure that there are no trailing empty spaces
// on each line you print. E.g.
// 1 2 F 4 B F 7 8 F B
// 1 F 3 F 5 F B F 9 F 11 F 13 FB 15
// ---------------------------PROPOSED SOLUTION-----------------------------
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func Fizzbuzz(N int) (output string) {
var (
A = N%3 == 0
B = N%5 == 0
)
switch {
case A && B:
output = "FB"
case A:
output = "F"
case B:
output = "B"
default:
output = fmt.Sprintf("%v", N)
}
return
}
func openFile(name string) *os.File {
file, err := os.Open(name)
if err != nil {
log.Fatalf("failed opening %s for writing: %s", name, err)
}
return file
}
func Readln(r *bufio.Reader) {
line, prefix, err := r.ReadLine()
if err != nil {
log.Fatalf("failed reading a line: %v", err)
}
if prefix {
log.Printf("Line is too big for buffer, only first %d bytes returned", len(line))
}
}
func WriteString(w *bufio.Writer) {
if n, err := w.WriteString(*Fizzbuzz); err != nil {
log.Fatalf("failed writing string: %s", err)
} else {
log.Printf("Wrote string in %d bytes", n)
}
}
func main() {
file := openFile(os.Args[1])
defer file.Close()
fi := bufio.NewReader(file)
Readln(fi)
fo := bufio.NewWriter(file)
defer fo.Flush()
WriteString(fo)
}
Go-Playground
* as a unary operator is used to dereference (or "indirect") a pointer. Fizzbuzz is a function, not a pointer. That is why the compiler says:
Invalid indirect of type func (int) string
What you really want to do is call the function: Fizzbuzz()
So line:
if fizzbuzz, err := w.WriteString(*Fizzbuzz); err != nil {
should be:
if fizzbuzz, err := w.WriteString(Fizzbuzz()); err != nil{
It is not very idiomatic to call the first return of writestring something like fizzbuzz. Normally we name it "n".
if n, err := w.WriteString(Fizzbuzz()); err != nil{

Which scan to use to read floats from a string?

This seems almost right but it chokes on the newline.
What's the best way to do this?
package main
import (
"fmt"
"strings"
)
func main() {
var z float64
var a []float64
// \n gives an error for Fscanf
s := "3.25 -12.6 33.7 \n 3.47"
in := strings.NewReader(s)
for {
n, err := fmt.Fscanf(in, "%f", &z)
fmt.Println("n", n)
if err != nil {
break
}
a = append(a, z)
}
fmt.Println(a)
}
Output:
n 1
n 1
n 1
n 0
[3.25 -12.6 33.7]
Update:
See the answer from #Atom below. I found another way which is to break if the error is EOF, and otherwise just ignore it. It's just a hack, I know, but I control the source.
_, err := fmt.Fscanf(in, "%f", &z)
if err == os.EOF { break }
if err != nil { continue }
If you are parsing floats only, you can use fmt.Fscan(r io.Reader, a ...interface{}) instead of fmt.Fscanf(r io.Reader, format string, a ...interface{}):
var z float64
...
n, err := fmt.Fscan(in, &z)
The difference between fmt.Fscan and fmt.Fscanf is that in the case of fmt.Fscan newlines count as space. The latter function (with a format string) does not treat newlines as spaces and requires newlines in the input to match newlines in the format string.
The functions with a format string give more control over the form of input, such as when you need to scan %5f or %10s. In this case, if the input contains newlines and it implements the interface io.RuneScanner you can use the method ReadRune to peek the next character and optionally unread it with UnreadRune if it isn't a space or a newline.
If your input is just a bunch of lines with floats separated by white space on each line, it might be easier to just read one line at a time from the file, run Sscanf on that line (assuming the number of floats on each line is fixed). But here's something that works in your example---there may be a way to make it more efficient.
package main
import (
"fmt"
"strings"
)
func main() {
var z float64
var a []float64
// \n gives an error for Fscanf
s := "3.25 -12.6 33.7 \n 3.47"
for _, line := range strings.Split(s, "\n") {
in := strings.NewReader(line)
for {
n, err := fmt.Fscanf(in, "%f", &z)
fmt.Println("n", n)
if err != nil {
fmt.Printf("ERROR: %v\n", err)
break
}
a = append(a, z)
}
}
fmt.Println(a)
}

Resources