Skip to Content
Course content

117: Reading and Writing Files with os.Open and os.Create

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've been thinking about building a small utility to track my project's "Current Focus" so I don't lose my place when I switch contexts between different repositories. I figured the simplest way to do this is just to dump a string into a local text file and read it back when I start my editor. Let's try to build that now.

Actually getting something on disk

First, I need to create a file. I'll use os.Create. It's a straightforward function, but let's see what it actually gives us.

package main

import (
	"fmt"
	"os"
)

func main() {
	f, err := os.Create("focus.txt")
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	
	f.WriteString("Working on the API authentication layer\n")
	fmt.Println("File written successfully.")
}

That works, and the file appears. But here's the thing: I'm a bit of a stickler for resource management. If I keep opening files without closing them, I'm eventually going to hit the operating system's limit on open file descriptors. In a small script, it doesn't matter, but in a long-running server, it's a disaster. I need to ensure that file handle f is closed regardless of how the function exits.

I'll add a defer f.Close() immediately after the error check. This is the standard Go way to say, "I'm done with this the moment the surrounding function returns."

f, err := os.Create("focus.txt")
if err != nil {
	fmt.Println("Error:", err)
	return
}
defer f.Close() // Now I can breathe easy.

Getting that data back

Now that I can write my focus, I need to read it. I'll use os.Open. I've noticed a lot of beginners confuse os.Create with os.Open. The big difference? os.Create truncates the file if it already exists (it wipes it clean), while os.Open opens it in read-only mode.

Let's try to read it:

f, err := os.Open("focus.txt")
if err != nil {
	fmt.Println("Error:", err)
	return
}
defer f.Close()

fmt.Println("File opened!")

Wait. I ran this and... nothing happened except the "File opened!" message. I realized I made a classic mistake: I opened the handle to the file, but I didn't actually read any data from it. os.Open just gives me a pointer to the file; it doesn't load the contents into memory.

To get the actual text, I need to use something that can read from that file handle. Since I know my focus file is small, I'll use io.ReadAll. It'll suck the entire content of the file into a byte slice.

import (
	"fmt"
	"io"
	"os"
)

// ... inside main ...
f, err := os.Open("focus.txt")
if err != nil {
	fmt.Println("Error:", err)
	return
}
defer f.Close()

content, err := io.ReadAll(f)
if err != nil {
	fmt.Println("Read error:", err)
	return
}

fmt.Printf("Current Focus: %s", content)

Dealing with the "missing file" panic

I just tried running my read logic before I had ever run the write logic. Naturally, it crashed with a "no such file or directory" error. This is where we have to be intentional. In a real tool, I don't want the program to just quit; I want it to tell me that no focus has been set yet.

I can check the error returned by os.Open using os.IsNotExist(err). This allows me to distinguish between a catastrophic system failure and a simple "the file isn't there yet" scenario.

f, err := os.Open("focus.txt")
if err != nil {
	if os.IsNotExist(err) {
		fmt.Println("No focus file found. You're starting fresh!")
		return
	}
	fmt.Println("Serious error opening file:", err)
	return
}
defer f.Close()

Now the flow is robust. I can create a file, write to it, and read from it, all while managing my system resources and handling the case where the file doesn't exist. It's not the most complex system in the world, but these patterns—Open, Defer Close, Read—are the bedrock of almost every Go program that touches the disk.




📋 Practical Task

Build a Simple Log-to-File Logger

Create a program that simulates a system logger. Your program should do the following:

  • Define a string variable containing a simulated log message (e.g., "2023-10-27 10:00:00 - System Heartbeat: OK").
  • Use os.Create to create a file named system.log and write that message into it. Ensure the file is closed properly using defer.
  • Immediately after writing, use os.Open to open the system.log file.
  • Read the contents of the file using io.ReadAll and print the result to the console to verify that the log was stored correctly.
  • Add a check using os.IsNotExist to handle cases where the log file might be missing before you attempt to read it.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.