Home > Blockchain >  golang csv.write not writing but no errors
golang csv.write not writing but no errors

Time:09-22

I am trying to open an existing csv file and write to it; however, the file returns empty. This is my code.

file, err := os.Open("file.csv")
    if err != nil {
        log.WithError(err)
    }

    defer file.Close()

    w := csv.NewWriter(file)
    defer w.Flush()

    var headers = []string{"h1", "h2", "h3", "h4"}
    writeHeadersErr := w.Write(headers)
    if writeHeadersErr != nil {
        log.WithError(writeHeadersErr)
        file.Close()
    }

Not sure how to approach this as I do not see any errors logged.

CodePudding user response:

You need to check the error from Flush():

w.Flush()

if err := w.Error(); err != nil {
    log.Fatal(err) // write file.csv: bad file descriptor
}

which will show that you are opening a file for reading not writing. So to fix:

//file, err := os.Open("file.csv") // read file
file, err := os.Create("file.csv") // create file
if err != nil {
    log.Fatal(err)
}

https://play.golang.org/p/QhpYnrc7cmR

CodePudding user response:

os.Open opens a file in read-only mode. You should use os.OpenFile instead:

os.OpenFile("file.csv", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

FYI, note that os.Create also works but it will truncate the file if it already exists, as you mention. This may or may not be what you want.

As of why you see no error, it's because writes are buffered and the content isn't actually written until you call w.Flush. This is mentioned in w.Write documentation:

Writes are buffered, so Flush must eventually be called to ensure that the record is written to the underlying io.Writer.

Though w.Flush itself is deferred in your code, and doesn't return an error anyway. You can check for errors with w.Error().

If you place the two calls at the end of your function, as follows, you will eventually see the error:

    file, err := os.Open("file.csv")
    if err != nil {
        log.WithError(err)
    }
    defer file.Close()

    w := csv.NewWriter(file)
    // ... write to the file
    w.Flush()
    err = w.Error() // write file.csv: bad file descriptor

And as a matter of fact the error means that you opened the file with the wrong mode flags. More details in: Golang bad file descriptor

If you want to keep deferring w.Flush(), place it together with w.Error() in a function literal, which if used in combination with named return params, allows you to propagate the error, if any.

For example:

func writeToCsv() (err error) {
    
    // ...open file
    w := csv.NewWriter(file)
    defer func() {
        w.Flush()
        err = w.Error()
    }()
    // ...rest of the function
}
  • Related