Go 将csv.Reader()用于“chan字符串”的有效方法
我有一个chan字符串,其中每个条目都是一个CSV日志行,我想将其转换为列[]字符串,目前我正在为每个条目无效地创建一个CSV.NewReaderString.NewReaderi,这看起来比实际需要的工作量大得多:Go 将csv.Reader()用于“chan字符串”的有效方法,go,Go,我有一个chan字符串,其中每个条目都是一个CSV日志行,我想将其转换为列[]字符串,目前我正在为每个条目无效地创建一个CSV.NewReaderString.NewReaderi,这看起来比实际需要的工作量大得多: for i := range feederChan { r := csv.NewReader(strings.NewReader(i)) a, err := r.Read() if err != nil { // log error...
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内容以某种方式传送到实现“io.Reader”接口的东西上?使用以下方法将字符串通道转换为读卡器:
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
}
如果应用程序假定换行符分隔从通道接收到的值,则在每个接收到的值后附加一个换行符:
...
var ok bool
r.buf, ok = <-r.c
if !ok {
// Return eof on channel closed
return 0, io.EOF
}
r.buf += "\n"
...
另一种方法是使用io.Pipe和goroutine将通道转换为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(&chanReader{c: feederChan})
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
r := csv.NewReader(createChanReader(feederChan))
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
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
}
当应用程序在将管道读取到EOF之前退出循环时,io.Pipe解决方案的第一次传递会泄漏GOROUTE。由于CSV读取器检测到语法错误、由于程序员错误或任何其他原因,应用程序可能会提前中断
要修复goroutine泄漏,请退出写入goroutine on write错误,并在读取完成后关闭管道读取器
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
}
像这样使用它:
r := csv.NewReader(&chanReader{c: feederChan})
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
r := csv.NewReader(createChanReader(feederChan))
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
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
}
.使用以下方法将字符串通道转换为读卡器:
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
}
如果应用程序假定换行符分隔从通道接收到的值,则在每个接收到的值后附加一个换行符:
...
var ok bool
r.buf, ok = <-r.c
if !ok {
// Return eof on channel closed
return 0, io.EOF
}
r.buf += "\n"
...
另一种方法是使用io.Pipe和goroutine将通道转换为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(&chanReader{c: feederChan})
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
r := csv.NewReader(createChanReader(feederChan))
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
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
}
当应用程序在将管道读取到EOF之前退出循环时,io.Pipe解决方案的第一次传递会泄漏GOROUTE。由于CSV读取器检测到语法错误、由于程序员错误或任何其他原因,应用程序可能会提前中断
要修复goroutine泄漏,请退出写入goroutine on write错误,并在读取完成后关闭管道读取器
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
}
像这样使用它:
r := csv.NewReader(&chanReader{c: feederChan})
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
r := csv.NewReader(createChanReader(feederChan))
for {
a, err := r.Read()
if err != nil {
// handle error, break out of loop
}
// do something with a
}
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
}
.尽管ThunderCat的答案非常有用,也很受欢迎,但我最终还是使用了mh cbon提到的io.Pipe,它更简单,看起来更高效,如下所述:
rp, wp := io.Pipe()
go func() {
defer wp.Close()
for i := range feederChan {
fmt.Fprintln(wp, i)
}
}()
r := csv.NewReader(rp)
for { // keep reading
a, err := r.Read()
if err == io.EOF {
break
}
// do stuff with 'a'
// ...
}
io.Pipe是同步的,应该相当高效:它将数据从写入程序传输到读取器;我将csv.NewReader输入到阅读器部分,并创建了一个goroutine,将chan的文字输出到编写部分
非常感谢
编辑:ThunderCat在我发布这篇文章后,在他的答案中加入了io.Pipe方法,我想。。。他的答案更全面,因此被接受。尽管ThunderCat的答案非常有用和受人赞赏,但我最终还是使用了mh cbon提到的io.Pipe,它更简单,看起来更有效,如下所述:
rp, wp := io.Pipe()
go func() {
defer wp.Close()
for i := range feederChan {
fmt.Fprintln(wp, i)
}
}()
r := csv.NewReader(rp)
for { // keep reading
a, err := r.Read()
if err == io.EOF {
break
}
// do stuff with 'a'
// ...
}
io.Pipe是同步的,应该相当高效:它将数据从写入程序传输到读取器;我将csv.NewReader输入到阅读器部分,并创建了一个goroutine,将chan的文字输出到编写部分
非常感谢
编辑:ThunderCat在我发布这篇文章后,在他的答案中加入了io.Pipe方法,我想。。。他的回答更全面,因此被接受。创建一次csv.Reader,然后以某种方式将内容输入到它。您可以使用io.Pipe,将chan的内容放入写入部分,将读取部分交给csv阅读器,排空csv阅读器。创建csv.reader一次,然后以某种方式向其提供chan内容。您可以使用io.Pipe,将chan的内容放入写入部分,将读取部分交给csv读取器,然后排空csv读取器。