Reading a text file

If I use “io/ioutil” package with the code like:

    dat, err := ioutil.ReadFile("file_name")

Do I need to close the “file” after reading it(just like what should be done in c/c++)? Or Go would close that file automatically after reading it?

Hey @Jaedong_Tang, there is no need to close the file if you read the file using ioutil.ReadFile.

Here is the first part of ioutil.ReadFile's body, so you can see that it open’s and closes the file underneath, however if you have opened the file somewhere else separately, you’ll still need to close the file:

f, err := os.Open(filename)
if err != nil {
	return nil, err
}
defer f.Close()

Lastly, if however you actually meant to ask about ioutil.ReadAll and not ioutil.ReadFile to read from a file once the file is already opened, in that case you DO need to close the file.

2 Likes

Do I need to close the “file” after reading it(just like what should be done in c/c++)? Or Go would close that file automatically after reading it?

Not in this case. ReadFile is a wrapper that handles opening the path you provide, and returns the contents as a byte. There is nothing to close as none of the arguments you provide or the value the function returns has a Close method.

1 Like

Thanks for your reply. I also have an another question. If I create a new file by

f, err = os.Create(filename)

does Go automatically open it for me? So that I could directly read/write the file without opening it explicitly. If this is the case, I should close it after doing the i/o jobs?

Hey @Jaedong_Tang,

Yes, that’s correct. When calling os.Create, f will now be an opened file handle to the newly created file as long as there were no errors. Also yes to your second question. Usually you would write something like this when creating a new file:

f, err := os.Create(filename)
if err != nil {
	log.Fatalln(err)
}
defer f.Close()

// Do stuff with file.

To be pedantic (sorry), deferring the Close call is not optimal for files that you write to. Writes may not happen until they are flushed by the Close, so the error returned from there is important to check.

3 Likes

You can enforce that with https://golang.org/pkg/os/#File.Sync though right?

Yeah, but you gotta check the error from that method as well as Close, so you’re not really buying much.

Ah yeah fair enough, good to keep in mind, thanks Dave…

I personally only use os.Create for creating files anyway but thought it was idiomatic to defer to call Close as long as you were calling Sync before the file was closed and checking errors from Sync, from when I first got in to Go and saw things like this: https://gobyexample.com/writing-files.

I guess you can’t just assume everything you read is good advice just because it’s on a popular website, that’s for sure.

What’s the point in calling f.Sync() if the error is ignored?

If you mean from on that site then I completely agree there’s no point lol, but I personally try to remember to check every single error and even when deferring I usually use defer func() { … }() and just check the errors there.

The examples on that site are unfortunately completely broken from an error checking point of view. I tried to fix a couple of them and filed pull requests, but with no action on them for years. I think it’s dead in the water.

2 Likes

Hey Jakob. I guess it’s just a shame that websites like that are the ones that beginners still go to learn… It’s a good thing that resources like the forum and the Slack channel exist…

Which error do you mean? From the os.Create() or the f.Close() call?

Close. Deferring Create is unusual. :slight_smile:

1 Like

So to use @radovskyb 's zip archive writer code as an example, how would that code need to be changed to follow the rule explained?

I would guess moving both the defer calls to the end of the function scope to be something like this?

err := w.Close()
if err != nil {
    log.Writeln(err) //Note: not log.Fatal
}

err := f.Close()
if err != nil {
    log.Fatal(err)
}

   

(BTW, thanks for your excellent examples Benjamin :beer:)

1 Like

Hey @pieterlouw, I wrote a lot of those examples a long time ago, but I will update those to handle the error properly later if you like :slight_smile: There are lots of examples that use defer f.Close() but I should really change them to handle the error inside of the defer block.

P.S I wouldn’t just move them to the bottom, because with defer they will get run no matter what, but if there is a fatal error mid way through, close will not be called unless it is called at every stage where an error can occur.

Didn’t mean to call you out on your code, and I’ll help to change the code as soon as I understand properly this problem

Oh no trust me it’s completely fine :slight_smile: There are literally heaps of examples that need to be re-written since majority of them are from when I first started learning Go, so of course a lot will be imperfect!

1 Like