Golang:在处理CSV时,重新格式化单行?

时间:2017-05-16 18:55:29

标签: csv go

我的golang CSV处理例程几乎完全复制了Package CSV示例:

func processCSV(path string){

    file:= utils.OpenFile(path)
    reader:= csv.NewReader(file)
    reader.LazyQuotes = true

    cs:= []*Collision{} //defined elsewhere

    for {

        line, err := reader.Read()

        //Kill processing if we're at EOF
        if err == io.EOF {
            break
        }

        c := get(line) //defined elsewhere
        cs= append(cs, c)
    }

    //Do other stuff...
}

代码效果很好,直到遇到格式错误的(?)CSV行,通常看起来像这样:

item1,item2," item3,"有奇怪的引用""," item4",item5

csvReader.LazyQuotes = true选项似乎没有提供足够的容差来根据需要读取此行。

我的问题是:我可以向csv读者询问原始行,以便我可以按摩"它拉出我需要的东西?我正在使用的文件中等大(约150mb),我不确定是否要重新执行它们,特别是因为每个文件只有几行有这样的问题。

感谢您的任何提示!

3 个答案:

答案 0 :(得分:0)

查看csv.Read()的实现,您无法使用csv包执行所需操作。它使用模块私有函数parseRecord()来完成艰苦的工作。

我认为您需要编写自己的CSV阅读器来处理这种情况,或者只是逐行预处理文件,以便将格式错误的项目从"替换为\"(其中csv包可以正确处理)。

答案 1 :(得分:0)

据我所知,encoding/csv没有提供任何此类功能,因此您可以查找某些第三方csv程序包,或者您可以自己实施解决方案。

如果你想去DIY路线,我可以给你一个提示,不管它是否是你应该实施的好提示取决于你。

你可以实现一个io.Reader来包装你的文件并跟踪读取的最后一行,然后每次你因为格式错误的csv而输入错误,你可以用你的读者重读那行,按摩它,添加到结果,并让循环继续,好像什么也没发生。

以下是processCSV将如何更改的示例:

func processCSV(path string){

    file := utils.OpenFile(path)
    myreader := NewMyReader(file)
    reader := csv.NewReader(myreader)
    reader.LazyQuotes = true

    cs:= []*Collision{} //defined elsewhere

    for {

        line, err := reader.Read()

        //Kill processing if we're at EOF
        if err == io.EOF {
            break
        }

        // malformed csv
        if err != nil {
            // Just reread the last line and on the next iteration of
            // the loop myreader.Read should continue returning bytes 
            // that come after this malformed line to the csv.Reader.
            l, err := myreader.CurrentLine()
            if err != nil {
                panic(err)
            }

            // massage the malformed csv line
            line = fixcsv(l) 
        }

        c := get(line) //defined elsewhere
        cs= append(cs, c)
    }

    //Do other stuff...
}

答案 2 :(得分:0)

我"解决了#34;这个问题使用了来自mkopriva的提示和来自Go的CSV解析代码的公然复制。如果我读得正确,那么Go的CSV解析器对它所认为的线条非常聪明。当我编写了一个简单的CSV解析器时,我会在新行上拆分文件,然后对它们进行处理。 Go的解析器更智能,并且包括引用字段本身可能包含新行的可能性。在那些情况下,我的代码会失败,他们的代码会起作用。

喂养"线" Go的解析器有点棘手,因为它通过流来读取行开始和结束模式并沿途提取字段。我所做的是劫持代码并添加一个跟踪代码认为是一行的流的开头和结尾的变量。我的补充可能有问题,但似乎对我有用。如果它有帮助,这是我采取的步骤:

1)复制CSV source并完整粘贴到我的项目中。

2)添加一个新字段以输入Reader struct {}:

type Reader struct {
    ...
    // The i'th field starts at offset fieldIndexes[i] in lineBuffer.
    fieldIndexes []int

    CurrentLine []byte //Added struct field to hold onto the line

    ...
}

3)在readRune()中,捕获字节,如下所示:

func (r *Reader) readRune() (rune, error) {
    r1, _, err := r.r.ReadRune()
    r.CurrentLine = append(r.CurrentLine, byte(r1)) //added: stores bytes as processed
    ...
}

4)在Read()中,为每一行重置CurrentLine,如下所示:

func (r *Reader) Read() (record []string, err error) {

    r.CurrentLine = []byte{} //added: reset line capturing

    ...
}

添加了这些项目后,根据mkopriva的建议,我可以在出现解析错误时获取当前行:

...
if err != nil {

    line = fixCSV(csvReader.CurrentLine)
    continue

}
...