Reputation: 23
I'm learning go language and try to rewrite some of my Python code using golang. I wrote a generator function that reads a text file line by line and send (using yield keyword) only "valid" lines (blank lines are ignored, uncompleted lines are recomposed).
Sample file (myfile.txt):
#123= FOOBAR(1.,'text');
#126= BARBAZ('poeazpfodsp',
234,56);
parse.py:
#!/usr/bin/python
def validlines(filename):
with open(filename) as fdin:
buff = ''
for line in fdin.readlines():
line = line.strip()
if line == '':
continue
buff += line
if line[-1] != ';':
continue
yield buff
buff = ''
fdin.close()
for line in validlines('myfile.txt'):
print(line)
displays:
#123= FOOBAR(1.,'text');
#126= BARBAZ('poeazpfodsp',234,56);
Now, I try to do it the same way using a closure in golang:
parse.go:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func validLines(filename string) (func() (string, bool)) {
file, _ := os.Open(filename)
scanner := bufio.NewScanner(file)
return func() (string, bool) {
buff := ""
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
if line == "" {
continue
}
buff += line
if line[len(line)-1] != ';' {
continue
}
return buff, true
}
file.Close()
return "", false
}
}
func main() {
vline := validLines("myfile.txt")
for line, ok := vline(); ok; {
fmt.Println(line)
}
}
displays:
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
#123= FOOBAR(1.,'text');
...
What's the right way to do it in golang?
Upvotes: 2
Views: 1726
Reputation: 21
Just little addition to pav5000's answer, you should make the channel buffered:
c := make(chan string, 1)
Otherwise, it will be reading the whole file into a channel and there is no point in using it.
Upvotes: 2
Reputation: 143
In Go you can use channels instead of yield, it's very convenient.
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func ValidLines(filename string) (c chan string) {
c = make(chan string)
buff := ""
go func() {
file, err := os.Open(filename)
if err != nil {
close(c)
return
}
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
close(c)
return
}
line = strings.TrimSpace(line)
if line == "" {
continue
}
buff += line
if line[len(line)-1] != ';' {
continue
}
c <- buff
buff = ""
}
}()
return c
}
func main() {
for line := range ValidLines("myfile.txt") {
fmt.Println(line)
}
}
Upvotes: 6
Reputation: 1096
small change:
func main() {
vline := validLines("myfile.txt")
line, ok := vline()
for ok {
fmt.Println(line)
line, ok = vline()
}
}
Upvotes: 1
Reputation: 12256
This is the for
loop of your main which is the problem. With this syntax, here is what it does:
line
and ok
with a first call to vline()
ok
is true, run the loop onceSo, your problem is that you never update line
and ok
. Here is the correct version:
for line, ok := vline(); ok; line, ok = vline() { ... }
Upvotes: 1