gpt4 book ai didi

go - 将 csv.Reader() 用于 "chan string"的有效方法

转载 作者:IT王子 更新时间:2023-10-29 01:51:55 25 4
gpt4 key购买 nike

我有一个“chan string”,其中每个条目都是一个 CSV 日志行,我想将其转换为列“[]string”,目前我正在(效率低下)创建一个 csv.NewReader(strings.NewReader (i)) 对于每个项目,看起来比实际需要做的工作多得多:

for i := range feederChan {
r := csv.NewReader(strings.NewReader(i))
a, err := r.Read()
if err != nil {
// log error...
continue
}
// then do stuff with 'a'
// ...
}

所以,如果有更有效的方法来做到这一点,我真的很感激分享,比如创建一次 csv.Reader,然后以某种方式向它提供 chan 内容(将“chan”内容流式传输到实现“io.阅读器界面?)。

最佳答案

使用以下代码将字符串 channel 转换为读取器:

type chanReader struct {
c chan string
buf string
}

func (r *chanReader) Read(p []byte) (int, error) {

// Fill the buffer when we have no data to return to the caller
if len(r.buf) == 0 {
var ok bool
r.buf, ok = <-r.c
if !ok {
// Return eof on channel closed
return 0, io.EOF
}
}

n := copy(p, r.buf)
r.buf = r.buf[n:]
return n, nil
}

像这样使用它:

r := csv.NewReader(&chanReader{c: feederChan})
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}

Run it on the playground

如果应用程序假定换行符分隔从 channel 接收的值,则将换行符附加到每个接收到的值:

        ...
var ok bool
r.buf, ok = <-r.c
if !ok {
// Return eof on channel closed
return 0, io.EOF
}
r.buf += "\n"
...

+= "\n" 复制字符串。如果这不能满足应用程序的效率要求,则引入一个新字段来管理行分隔符。

type chanReader struct {
c chan string // source of lines
buf string // the current line
nl bool // true if line separator is pending
}

func (r *chanReader) Read(p []byte) (int, error) {

// Fill the buffer when we have no data to return to the caller
if len(r.buf) == 0 && !r.nl {
var ok bool
r.buf, ok = <-r.c
if !ok {
// Return eof on channel closed
return 0, io.EOF
}
r.nl = true
}

// Return data if we have it
if len(r.buf) > 0 {
n := copy(p, r.buf)
r.buf = r.buf[n:]
return n, nil
}

// No data, return the line separator
n := copy(p, "\n")
r.nl = n == 0
return n, nil
}

Run it on the playground .

另一种方法是使用 io.Pipe 和 goroutine 将 channel 转换为 io.Reader,如问题评论中所建议的那样。这种方法的第一步是:

var nl = []byte("\n")

func createChanReader(c chan string) io.Reader {
r, w := io.Pipe()
go func() {
defer w.Close()
for s := range c {
io.WriteString(w, s)
w.Write(nl)
}
}
}()
return r
}

像这样使用它:

r := csv.NewReader(createChanReader(feederChan))
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}

当应用程序在将管道读取到 EOF 之前退出循环时,这个 io.Pipe 解决方案的第一遍会泄漏一个 goroutine。应用程序可能会提前中断,因为 CSV 阅读器检测到语法错误、应用程序因程序员错误或任何其他原因而崩溃。

要修复 goroutine 泄漏,请在写入错误时退出写入 goroutine,并在完成读取后关闭管道读取器。

var nl = []byte("\n")

func createChanReader(c chan string) *io.PipeReader {
r, w := io.Pipe()
go func() {
defer w.Close()
for s := range c {
if _, err := io.WriteString(w, s); err != nil {
return
}
if _, err := w.Write(nl); err != nil {
return
}
}
}()
return r
}

像这样使用它:

cr := createChanReader(feederChan)
defer cr.Close() // Required for goroutine cleanup
r := csv.NewReader(cr)
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}

Run it on the playground .

关于go - 将 csv.Reader() 用于 "chan string"的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52347816/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com