2

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.

2
  • You need to open the file in write mode play.golang.org/p/Gd-3n45d_HQ Commented Sep 20, 2021 at 16:18
  • 1
    @null still should check Write/Flush potential errors via csv.Writer.Error() Commented Sep 20, 2021 at 16:23

2 Answers 2

2

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
}
Sign up to request clarification or add additional context in comments.

2 Comments

modified it to be os.OpenFile("file.csv", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644), which worked!
yes if the file doesn't exist
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.