【问题标题】:golang: read file generatorgolang:读取文件生成器
【发布时间】:2016-05-26 10:23:36
【问题描述】:

我正在学习 go 语言并尝试使用 golang 重写我的一些 Python 代码。 我编写了一个生成器函数,它逐行读取文本文件并仅发送(使用 yield 关键字)“有效”行(空白行被忽略,未完成的行被重新组合)。

示例文件(myfile.txt):

#123= FOOBAR(1.,'text');

#126= BARBAZ('poeazpfodsp',
234,56);

解析.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) 

显示:

#123= FOOBAR(1.,'text');
#126= BARBAZ('poeazpfodsp',234,56);    

现在,我尝试在 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)
    }
}

显示:

#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');
...

在 golang 中正确的做法是什么?

【问题讨论】:

    标签: python go generator


    【解决方案1】:

    在Go中你可以使用channels来代替yield,非常方便。

    主包

    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)
        }
    }
    

    【讨论】:

    • 我建议在匿名函数的顶部使用defer close(c)
    【解决方案2】:

    只是对pav5000's answer 的一点补充,你应该让频道缓冲:
    c := make(chan string, 1)
    否则,它会将整个文件读入一个通道,并且没有使用它的意义。

    【讨论】:

      【解决方案3】:

      这是您的主要问题的for 循环。使用这种语法,它的作用如下:

      1. 首先调用vline() 初始化lineok
      2. 如果ok 为真,则运行一次循环
      3. 在块的末尾更新一些东西,然后转到 2。

      所以,你的问题是你永远不会更新lineok。这是正确的版本:

      for line, ok := vline(); ok; line, ok = vline() { ... }
      

      【讨论】:

      • 我正在寻找validLines函数的问题,非常感谢。
      【解决方案4】:

      零钱:

      func main() {
          vline := validLines("myfile.txt")
          line, ok := vline()
          for ok  {
              fmt.Println(line)
              line, ok = vline()
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-03-04
        • 1970-01-01
        • 2019-04-13
        • 2015-05-13
        • 2016-12-08
        • 2022-07-04
        • 2017-10-11
        • 1970-01-01
        • 2016-10-24
        相关资源
        最近更新 更多