解析包含标头块 "comments" 的 CSV 文件



我试图解析托管在远程位置的CSV文件,但令人恼火的是,该文件在文件顶部包含一些自述注释,格式如下:

######
# some readme text,
# some more, comments
###### 
01-02-03,123,foo,http://example.com 
04-05-06,789,baz,http://another.com

我试图使用以下代码来提取数据中的url,但由于顶部的注释,它抛出一个错误说wrong number of fields,大概它试图将它们解析为CSV内容。

type myData struct {
URL string `json:"url"`
}
func doWork() ([]myData, error) {
rurl := "https://example.com/some.csv"
out := make([]myData, 0)

resp, err := http.Get(rurl)
if err != nil {
return []myData{}, err
}
defer resp.Body.Close()
reader := csv.NewReader(resp.Body)
reader.Comma = ','
data, err := reader.ReadAll()
if err != nil {
return []myData{}, err
}
for _, row := range data {
out = append(out, myData{URL: row[4]})
}
return out, nil
}
func main() {
data, err := doWork()
if err != nil {
panic(err)
}
// do something with data
}

是否有办法跳过远程文件的前N行,或者让它忽略以#

开头的行?

哦,实际上我刚刚意识到我可以添加这个:

reader.Comment = '#' // ignores the line starting with '#'

与我当前的代码完美配合,但感谢其他建议。

方法很简单:不要试图将以'#'开头的行解释为CSV流的一部分;相反,可以将整个数据流视为两个流的连接:头和实际的CSV有效负载。

最简单的方法可能是使用bufio.Reader能够从其底层流读取行,并且它本身是io.Reader,因此您可以使csv.Reader读取而不是源流。

所以,你可以像这样滚动(不是真正的代码,未经测试):

import (
"bufio"
"encoding/csv"
"io"
"strings"
)
func parse(r io.Reader) ([]MyData, error) (
br := bufio.NewReader(r)
var line string
for {
s, err := br.ReadString('n')
if err != nil {
return nil, err
}
if len(s) == 0 || s[0] != '#' {
line = s
break
}
}
// At this point the line variable contains the 1st line of the CSV stream.
// Let's create a "multi reader" which reads first from that line
// and then — from the rest of the CSV stream.
cr := csv.NewReader(io.MultiReader(strings.NewReader(line), br))
cr.Comma = ','
data, err := cr.ReadAll()
if err != nil {
return nil, err
}
for _, row := range data {
out = append(out, myData{URL: row[4]})
}
return out, nil
}

最新更新