How to scan a file line by line in an anonymous function and pass it down a channel

huangapple go评论76阅读模式
英文:

How to scan a file line by line in an anonymous function and pass it down a channel

问题

我想编写一个函数,从文件中读取行,并将其发送到一个通道中进行进一步处理。我对Go语言还不熟悉,以下是我根据大部分教科书示例编写的读取函数:

func reader(file string) <-chan string {
    out := make(chan string)

    f, err := os.Open(file)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    s := bufio.NewScanner(f)
    go func() {
        for s.Scan() {
            out <- s.Text()
        }
        close(out)
    }()
    
    return out
}

main()函数中,我将文件指针传递给读取函数,并尝试按如下方式读取out通道:

func main() {
    out := reader(*f)
    for range out {
        fmt.Println(<-out)
    }
}

我没有编译或运行时错误,但输出为空。如果我不将for循环放入goroutine中,我可以在reader()函数中打印文件而没有任何问题。

我还尝试将Scanner、通道或两者都传递给匿名函数的reader()函数。但这并没有解决问题。有人能解释一下为什么这段代码不起作用,以及如何解决这个问题吗?

英文:

I want to write a function that reads lines from a file and sends then into a channel for further processing. I'm new to Go and here is the reader function I came up with following mostly textbook examples:

func reader(file string) &lt;-chan string {
	out := make(chan string)

	f, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	go func() {
		for s.Scan() {
			out &lt;- s.Text()
		}
		close(out)
	}()
	
	return out
}

In the main() function I pass a file pointer into the reader function and try to drain the out channel as follows:

func main() {
    out := reader(*f)
    for range out {
        fmt.Println(&lt;-out)
    }
}

I get no comilation or runtime errors, however, the output is empty. If I don't put the for loop into a goroutine I can print the file from withing the reader() function w/o any problems.

I've furthermore tried to pass the Scanner, channel or both in the reader() function into the anonymous function. But it didn't solve the problem. Can anyone explain please why this code does not work and how to remedy it?

答案1

得分: 0

你在读取文件之前关闭了它。

你只打印了每隔一行。

将defer语句移到实际发生读取的闭包中。

func reader(file string) <-chan string {
    out := make(chan string)

    f, err := os.Open(file)
    if err != nil {
        log.Fatal(err)
    }

    s := bufio.NewScanner(f)
    go func() {
        defer f.Close()
        defer close(out)
        for s.Scan() {
            out <- s.Text()
        }
        if err := s.Err(); err != nil {
            fmt.Println("error reading file:", err)
        }
    }()

    return out
}

func main() {
    out := reader(os.Args[1])
    for line := range out {
        fmt.Println(line)
    }
}
英文:

You're closing the file before you read it.

You're only printing every other line.

Move the defer into the closure where the reads actually happen.

func reader(file string) &lt;-chan string {
	out := make(chan string)

	f, err := os.Open(file)
	if err != nil {
		log.Fatal(err)
	}

	s := bufio.NewScanner(f)
	go func() {
		defer f.Close()
		defer close(out)
		for s.Scan() {
			out &lt;- s.Text()
		}
		if err := s.Err(); err != nil {
			fmt.Println(&quot;error reading file:&quot;, err)
		}
	}()

	return out
}

Then print the value from the range clause, not a second value received within the for loop:

func main() {
	out := reader(os.Args[1])
	for line := range out {
		fmt.Println(line)
	}
}

huangapple
  • 本文由 发表于 2017年9月7日 22:16:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/46098680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定