Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
117: Reading and Writing Files with os.Open and os.Create
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.Createto create a file namedsystem.logand write that message into it. Ensure the file is closed properly usingdefer. - Immediately after writing, use
os.Opento open thesystem.logfile. - Read the contents of the file using
io.ReadAlland print the result to the console to verify that the log was stored correctly. - Add a check using
os.IsNotExistto handle cases where the log file might be missing before you attempt to read it.
There are no comments for now.