Go bufio ReadString in loop is infinite - go

I have the next code:
resp, err := http.Get("https://www.google.com")
if err != nil{
panic(err)
}
r := bufio.NewReader(resp.Body)
for v, e := r.ReadString('\n'); e == nil; {
fmt.Println(v)
}
So, I want to read responce body in loop but reader r reads first line of Body infinitely.
While in the same time, this code works fine:
v, e := r.ReadString('\n')
for e == nil {
fmt.Println(v)
v, e = r.ReadString('\n')
}
Can someone explain why the first solution has such behaviour?

Package bufio
import "bufio"
func (*Reader) ReadString
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). ReadString returns err != nil if and only if
the returned data does not end in delim. For simple uses, a Scanner
may be more convenient.
This is an XY problem: The XY problem is asking about your attempted solution rather than your actual problem.
Why didn't you take the advice, "For simple uses, a Scanner may be more convenient", given in the bufio.ReadString documentation?
Proper use of bufio.ReadString is complicated, even when you know how to use for loops. See function reader.
Proper use of bufio.Scannner is simple, even if you don't know how to use for loops. See function scanner.
For example,
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"strings"
)
func reader(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// ReadString
r := bufio.NewReader(resp.Body)
for {
line, err := r.ReadString('\n')
if len(line) == 0 && err != nil {
if err == io.EOF {
break
}
return err
}
line = strings.TrimSuffix(line, "\n")
fmt.Println(line)
if err != nil {
if err == io.EOF {
break
}
return err
}
}
return nil
}
func scanner(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Scanner
s := bufio.NewScanner(resp.Body)
for s.Scan() {
line := s.Text()
fmt.Println(line)
}
if s.Err() != nil {
return err
}
return nil
}
func main() {
url := "https://www.example.com"
fmt.Println("\nReader:\n")
err := reader(url)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println("\nScanner:\n")
err = scanner(url)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println("\n")
}
Playground: https://play.golang.org/p/e0WY_aNxW8

Structure of the loop is:
for init; condition; post { }
The init part of the loop is called only once, at the beginning. That means that the...
v, e := r.ReadString('\n')
...part from your loop is called only once, which explains why your loop implementation reads only the first line from r and why e is always nil, resulting in an infinite loop.
You may want to do something like this instead:
for v, e := "", (error)(nil); e == nil; {
v, e = r.ReadString('\n')
fmt.Println(v)
}
Or if that looks weird to you, something like this:
var v string
var e error
for ; e == nil; {
v, e = r.ReadString('\n')
fmt.Println(v)
}
More info here:
https://golang.org/doc/effective_go.html#for
https://golang.org/ref/spec#For_statements

Related

how to get multiple line inputs in golang - interview coding

For the below type of inputs in golang coding interviews, what is the best way to get the input?
Input:
3
hello elloh
test estt
tier riet
I found two methods:
Method 1:
reader := bufio.NewReader(os.Stdin)
var lines []string
for {
line,err := reader.ReadString('\n') //this reads only one read
if err != nil {
log.Fatal(err)
}
if len(strings.TrimSpace(line)) == 0 {
break
}
line_s := strings.Split(line, " ")
lines = append(lines, line_s...)
}
Method 2:
bytes, err := ioutil.ReadAll(os.Stdin)
fmt.Println(len(bytes))
if err == nil {
input := strings.Split(string(bytes), "\n")
count, _ := strconv.Atoi(input[0])
fmt.Println(input)
var lines []string
for i := 1; i < count; i++ {
line := strings.Split(input[i], " ")
lines = append(lines, line...)
}
fmt.Println(lines)
}
But not sure how to end getting input from stdin in Method2.
Please suggest the best method to get input.
Use bufio.Scanner to read input. Use a function to encapsulate complexity and implementation details. For example,
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func readData(s *bufio.Scanner) ([][]string, error) {
var data [][]string
if !s.Scan() {
return nil, s.Err()
}
nLine, err := strconv.Atoi(strings.TrimSpace(s.Text()))
if err != nil {
return nil, err
}
for ; nLine > 0 && s.Scan(); nLine-- {
data = append(data, strings.Fields(s.Text()))
}
if err := s.Err(); err != nil {
return nil, err
}
if nLine != 0 {
err := fmt.Errorf("missing %d lines of data", nLine)
return nil, err
}
return data, nil
}
func main() {
s := bufio.NewScanner(os.Stdin)
data, err := readData(s)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(len(data))
for _, datum := range data {
fmt.Println(datum)
}
}
https://go.dev/play/p/0Xwp3-hwGyK
3
hello elloh
test estt
tier riet
3
[hello elloh]
[test estt]
[tier riet]

Golang nested map filter

package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
fmt.Println(fecthData())
}
func fecthData() (map[string]interface{}, error) {
body := strings.NewReader("dil_kodu=tr")
req, err := http.NewRequest("POST", "https://www.haremaltin.com/dashboard/ajax/doviz", body)
if err != nil {
// handle err
return nil, err
}
req.Header.Set("X-Requested-With", "XMLHttpRequest")
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
return nil, err
}
defer resp.Body.Close()
jsonData, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
return nil, err
}
var data map[string]interface{}
err = json.Unmarshal(jsonData, &data)
if err != nil {
return nil, err
}
return data, nil
}
You can see full code above and I have a go response as below and it is nested map as you see and want to reach "data-ATA5_ESKI-satis" value which is 34319. Is there anybody to help me.
Thank you for your time
A part of response below:
map[data:map[AEDTRY:map[alis:4.6271 code:AEDTRY dir:map[alis_dir: satis_dir:] dusuk:4.7116 kapanis:4.6224 satis:4.7271 tarih:17-06-2022 19:41:45 yuksek:4.7276] AEDUSD:map[alis:0.2680 code:AEDUSD dir:map[alis_dir: satis_dir:] dusuk:0.27 kapanis:0.268 satis:0.2700 tarih:17-06-2022 19:30:02 yuksek:0.27]... ALTIN:map[alis:1024.790 code:ALTIN dir:map[alis_dir:down satis_dir:down] dusuk:1029.05 kapanis:1032.13 satis:1030.650 tarih:17-06-2022 19:41:58 yuksek:1040] ATA5_ESKI:map[alis:33869 code:ATA5_ESKI dir:map[alis_dir:down satis_dir:down] dusuk:34266 kapanis:34112 satis:34319 tarih:17-06-2022 19:41:58 yuksek:34630] XPTUSD:map[alis:933 code:XPTUSD dir:map[alis_dir: satis_dir:] dusuk:936 kapanis:953 satis:936 tarih:17-06-2022 19:41:58 yuksek:957]] meta:map[fiyat_guncelleme:2000 fiyat_yayini:web_socket time:1.655484118278e+12 time_formatted:]]
for _, v := range data { // we need value part of the map
m, ok := v.(map[string]interface{}) // we need the convert the map
// into interface for iteration
if !ok {
fmt.Printf("Error %T", v)
}
for k, l := range m {
if k == "ATA_ESKI"{ // the value we want is inside of this map
a, ok := l.(map[string]interface{}) // interface convert again
if !ok {
fmt.Printf("Error %T", v)
}
for b,c := range a{
if b == "satis"{ // the value we want
fmt.Println("Price is", c)
}
}
}
}
}
We can get the value adding this iteration before "return data, nil" at the end but I think there must be easier methods for this.

How to handle user input using os/exec in golang? I can't stop input stage

First, I build a command as exec.exe from this:
package main
import "fmt"
func main() {
var input string
fmt.Println("input a value")
fmt.Scanln(&input)
fmt.Println(input)
fmt.Println("input another value")
fmt.Scanln(&input)
fmt.Println(input)
}
Then I want to use os/exec pacage to run it:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("G:\\go_workspace\\GOPATH\\src\\pjx\\modules\\exec\\exec")
stdin, e := cmd.StdinPipe()
if e != nil {
panic(e)
}
stdout, e := cmd.StdoutPipe()
if e != nil {
panic(e)
}
if e:=cmd.Start();e!=nil {
panic(e)
}
stdin.Write([]byte("hello"))
var buf = make([]byte, 512)
n, e := stdout.Read(buf)
if e != nil {
panic(e)
}
fmt.Println(string(buf[:n]))
if e := cmd.Wait(); e != nil {
panic(e)
}
}
Finally I run it, and result will pause on the user input stage,like:
(in case picture is not loaded, they're paused on input stage)
please input a value:
1
12
232
Did I use cmd pipe in a wrong way?
The program is blocking because the fmt.Scanln in the subprocess is waiting for the \n character (an EOF would also cause it to return). To avoid blocking, your input should include two \ns, or you can just call 'stdin.Close()' to indicate that the input stream is done.
And since the subprocess calls Scanln and Println multiple times, a single call to stdout.Read may not read the complete output from the subprocess. You can keep calling the stdout.Read() until an io.EOF error is returned, or just use the ioutil.ReadAll.
func main() {
cmd := exec.Command("G:\\go_workspace\\GOPATH\\src\\pjx\\modules\\exec\\exec")
stdin, e := cmd.StdinPipe()
if e != nil {
panic(e)
}
stdout, e := cmd.StdoutPipe()
if e != nil {
panic(e)
}
if e := cmd.Start(); e != nil {
panic(e)
}
_, e = stdin.Write([]byte("hello\nworld\n"))
if e != nil {
panic(e)
}
stdin.Close()
out, _ := ioutil.ReadAll(stdout)
// or you can use a loop
//for {
// var buf = make([]byte, 512)
// n, e := stdout.Read(buf)
// if e == io.EOF {
// break
// }
// if e != nil {
// panic(e)
// }
// fmt.Println(string(buf[:n]))
//}
fmt.Println(string(out))
if e := cmd.Wait(); e != nil {
panic(e)
}
}
You need to be listening to the output of the executing program. When you write "hello", the program is probably still writing to its stdout. Try this:
go func() {
in := bufio.NewReader(stdout)
for {
s, err := in.ReadString('\n')
if err != nil {
return
}
fmt.Println(s)
}
}()
if e := cmd.Start(); e != nil {
panic(e)
}
stdin.Write([]byte("hello\n"))
stdin.Write([]byte("hello2\n"))
if e := cmd.Wait(); e != nil {
panic(e)
}
One way to do it
cmd := exec.Command("...") // Change to your path
stdin, err := cmd.StdinPipe()
if err != nil {
panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
buf := bytes.NewBuffer(nil)
// read the stdout continuously in a separate goroutine and capture it in buf
go func() {
io.Copy(buf, stdout)
}()
if err := cmd.Start(); err != nil {
panic(err)
}
stdin.Write([]byte("hello\n")) // Send \n to submit
stdin.Write([]byte("world\n"))
if err := cmd.Wait(); err != nil {
panic(err)
}
fmt.Fprint(os.Stdout, buf)
Result:
➜ go run main.go
input a value
hello
input another value
world

Read and merge two Yaml files in go language

Assuming we have two yaml files
master.yaml
someProperty: "someVaue"
anotherProperty: "anotherValue"
override.yaml
someProperty: "overriddenVaue"
Is it possible to unmarshall, merge, and then write those changes to a file without having to define a struct for every property in the yaml file?
The master file has over 500 properties in it that are not at all important to the service at this point of execution, so ideally I'd be able to just unmarshal into a map, do a merge and write out in yaml again but I'm relatively new to go so wanted some opinions.
I've got some code to read the yaml into an interface but i'm unsure on the best approach to then merge the two.
var masterYaml interface{}
yamlBytes, _ := ioutil.ReadFile("master.yaml")
yaml.Unmarshal(yamlBytes, &masterYaml)
var overrideYaml interface{}
yamlBytes, _ = ioutil.ReadFile("override.yaml")
yaml.Unmarshal(yamlBytes, &overrideYaml)
I've looked into libraries like mergo but i'm not sure if that's the right approach.
I'm hoping that after the master I would be able to write out to file with properties
someProperty: "overriddenVaue"
anotherProperty: "anotherValue"
Assuming that you just want to merge at the top level, you can unmarshal into maps of type map[string]interface{}, as follows:
package main
import (
"io/ioutil"
"gopkg.in/yaml.v2"
)
func main() {
var master map[string]interface{}
bs, err := ioutil.ReadFile("master.yaml")
if err != nil {
panic(err)
}
if err := yaml.Unmarshal(bs, &master); err != nil {
panic(err)
}
var override map[string]interface{}
bs, err = ioutil.ReadFile("override.yaml")
if err != nil {
panic(err)
}
if err := yaml.Unmarshal(bs, &override); err != nil {
panic(err)
}
for k, v := range override {
master[k] = v
}
bs, err = yaml.Marshal(master)
if err != nil {
panic(err)
}
if err := ioutil.WriteFile("merged.yaml", bs, 0644); err != nil {
panic(err)
}
}
For a broader solution (with n input files), you can use this function. I have used #robox answer to do my solution:
func ReadValues(filenames ...string) (string, error) {
if len(filenames) <= 0 {
return "", errors.New("You must provide at least one filename for reading Values")
}
var resultValues map[string]interface{}
for _, filename := range filenames {
var override map[string]interface{}
bs, err := ioutil.ReadFile(filename)
if err != nil {
log.Info(err)
continue
}
if err := yaml.Unmarshal(bs, &override); err != nil {
log.Info(err)
continue
}
//check if is nil. This will only happen for the first filename
if resultValues == nil {
resultValues = override
} else {
for k, v := range override {
resultValues[k] = v
}
}
}
bs, err := yaml.Marshal(resultValues)
if err != nil {
log.Info(err)
return "", err
}
return string(bs), nil
}
So for this example you should call it with this order:
result, _ := ReadValues("master.yaml", "overwrite.yaml")
In the case you have an extra file newFile.yaml, you could also use this function:
result, _ := ReadValues("master.yaml", "overwrite.yaml", "newFile.yaml")
DEEP MERGE TWO YAML FILES
package main
import (
"fmt"
"io/ioutil"
"sigs.k8s.io/yaml"
)
func main() {
// declare two map to hold the yaml content
base := map[string]interface{}{}
currentMap := map[string]interface{}{}
// read one yaml file
data, _ := ioutil.ReadFile("conf.yaml")
if err := yaml.Unmarshal(data, &base); err != nil {
}
// read another yaml file
data1, _ := ioutil.ReadFile("conf1.yaml")
if err := yaml.Unmarshal(data1, &currentMap); err != nil {
}
// merge both yaml data recursively
base = mergeMaps(base, currentMap)
// print merged map
fmt.Println(base)
}
func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
out := make(map[string]interface{}, len(a))
for k, v := range a {
out[k] = v
}
for k, v := range b {
if v, ok := v.(map[string]interface{}); ok {
if bv, ok := out[k]; ok {
if bv, ok := bv.(map[string]interface{}); ok {
out[k] = mergeMaps(bv, v)
continue
}
}
}
out[k] = v
}
return out
}

Read text file into string array (and write)

The ability to read (and write) a text file into and out of a string array is I believe a fairly common requirement. It is also quite useful when starting with a language removing the need initially to access a database. Does one exist in Golang?
e.g.
func ReadLines(sFileName string, iMinLines int) ([]string, bool) {
and
func WriteLines(saBuff[]string, sFilename string) (bool) {
I would prefer to use an existing one rather than duplicate.
As of Go1.1 release, there is a bufio.Scanner API that can easily read lines from a file. Consider the following example from above, rewritten with Scanner:
package main
import (
"bufio"
"fmt"
"log"
"os"
)
// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
// writeLines writes the lines to the given file.
func writeLines(lines []string, path string) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
w := bufio.NewWriter(file)
for _, line := range lines {
fmt.Fprintln(w, line)
}
return w.Flush()
}
func main() {
lines, err := readLines("foo.in.txt")
if err != nil {
log.Fatalf("readLines: %s", err)
}
for i, line := range lines {
fmt.Println(i, line)
}
if err := writeLines(lines, "foo.out.txt"); err != nil {
log.Fatalf("writeLines: %s", err)
}
}
Note: ioutil is deprecated as of Go 1.16.
If the file isn't too large, this can be done with the ioutil.ReadFile and strings.Split functions like so:
content, err := ioutil.ReadFile(filename)
if err != nil {
//Do something
}
lines := strings.Split(string(content), "\n")
You can read the documentation on ioutil and strings packages.
Cannot update first answer.
Anyway, after Go1 release, there are some breaking changes, so I updated as shown below:
package main
import (
"os"
"bufio"
"bytes"
"io"
"fmt"
"strings"
)
// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err error) {
var (
file *os.File
part []byte
prefix bool
)
if file, err = os.Open(path); err != nil {
return
}
defer file.Close()
reader := bufio.NewReader(file)
buffer := bytes.NewBuffer(make([]byte, 0))
for {
if part, prefix, err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
lines = append(lines, buffer.String())
buffer.Reset()
}
}
if err == io.EOF {
err = nil
}
return
}
func writeLines(lines []string, path string) (err error) {
var (
file *os.File
)
if file, err = os.Create(path); err != nil {
return
}
defer file.Close()
//writer := bufio.NewWriter(file)
for _,item := range lines {
//fmt.Println(item)
_, err := file.WriteString(strings.TrimSpace(item) + "\n");
//file.Write([]byte(item));
if err != nil {
//fmt.Println("debug")
fmt.Println(err)
break
}
}
/*content := strings.Join(lines, "\n")
_, err = writer.WriteString(content)*/
return
}
func main() {
lines, err := readLines("foo.txt")
if err != nil {
fmt.Println("Error: %s\n", err)
return
}
for _, line := range lines {
fmt.Println(line)
}
//array := []string{"7.0", "8.5", "9.1"}
err = writeLines(lines, "foo2.txt")
fmt.Println(err)
}
You can use os.File (which implements the io.Reader interface) with the bufio package for that. However, those packages are build with fixed memory usage in mind (no matter how large the file is) and are quite fast.
Unfortunately this makes reading the whole file into the memory a bit more complicated. You can use a bytes.Buffer to join the parts of the line if they exceed the line limit. Anyway, I recommend you to try to use the line reader directly in your project (especially if do not know how large the text file is!). But if the file is small, the following example might be sufficient for you:
package main
import (
"os"
"bufio"
"bytes"
"fmt"
)
// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err os.Error) {
var (
file *os.File
part []byte
prefix bool
)
if file, err = os.Open(path); err != nil {
return
}
reader := bufio.NewReader(file)
buffer := bytes.NewBuffer(make([]byte, 1024))
for {
if part, prefix, err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
lines = append(lines, buffer.String())
buffer.Reset()
}
}
if err == os.EOF {
err = nil
}
return
}
func main() {
lines, err := readLines("foo.txt")
if err != nil {
fmt.Println("Error: %s\n", err)
return
}
for _, line := range lines {
fmt.Println(line)
}
}
Another alternative might be to use io.ioutil.ReadAll to read in the complete file at once and do the slicing by line afterwards. I don't give you an explicit example of how to write the lines back to the file, but that's basically an os.Create() followed by a loop similar to that one in the example (see main()).
func readToDisplayUsingFile1(f *os.File){
defer f.Close()
reader := bufio.NewReader(f)
contents, _ := ioutil.ReadAll(reader)
lines := strings.Split(string(contents), '\n')
}
or
func readToDisplayUsingFile1(f *os.File){
defer f.Close()
slice := make([]string,0)
reader := bufio.NewReader(f)
for{
str, err := reader.ReadString('\n')
if err == io.EOF{
break
}
slice = append(slice, str)
}

Resources