File I/O
Most applications interact with files.
Common use cases:
-
Configuration files
-
JSON documents
-
Logs
-
Images
-
Databases
-
Binary data
Go provides both high-level and low-level APIs for working with files.
Reading an Entire File
The simplest way to read a file:
data, err := os.ReadFile("hello.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))Suppose hello.txt contains:
Hello GoOutput:
Hello GoWhy Does ReadFile Return []byte?
os.ReadFile() returns:
[]byteinstead of
stringbecause files are fundamentally sequences of bytes.
A file may contain:
-
Plain text
-
JSON
-
Images
-
PDFs
-
ZIP archives
-
Executables
-
Encrypted data
Returning []byte avoids assuming that every file contains text.
The programmer decides how to interpret the bytes.
For example:
Convert to text:
text := string(data)Decode JSON:
json.Unmarshal(data, &config)Decrypt:
plaintext := Decrypt(data)Writing a File
err := os.WriteFile(
"hello.txt",
[]byte("Hello Go"),
0644,
)
if err != nil {
fmt.Println(err)
}File Permissions
Example:
0644Meaning:
Owner : Read + Write
Group : Read
Others: ReadThis is the standard permission for normal files on Unix-like systems.
High-Level API
os.ReadFile() internally performs:
Open
↓
Read
↓
CloseThe caller never receives the file handle.
Therefore no cleanup is required.
Low-Level API
file, err := os.Open("hello.txt")
if err != nil {
return
}
defer file.Close()The caller owns the file handle and is responsible for closing it.
Reading Large Files
Loading an entire file into memory is not always appropriate.
Instead:
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println(line)
}This reads one line at a time.
Memory usage remains nearly constant regardless of file size.
Streaming
Streaming means:
Read
↓
Process
↓
Discard
↓
Read NextInstead of:
Load Entire File
↓
ProcessStreaming is essential for:
-
Large log files
-
Network protocols
-
Video processing
-
Data pipelines
Choosing the Right API
Small files:
os.ReadFile()Advantages:
-
Simple
-
Concise
-
Easy to read
Suitable for:
-
Configuration
-
JSON
-
Small text files
Large files:
os.Open()
bufio.ScannerAdvantages:
-
Constant memory usage
-
Better scalability
Suitable for:
-
Logs
-
Large CSV files
-
Streaming data
File Paths
Relative path:
hello.txtResolved relative to the current working directory.
Absolute paths:
Linux:
/home/user/file.txtWindows:
C:\Users\User\file.txtCurrent Working Directory
Useful for debugging:
wd, err := os.Getwd()
fmt.Println(wd)Many “file not found” errors are caused by running the program from an unexpected directory.
High-Level vs Low-Level APIs
Go often provides two levels of abstraction.
Example:
ReadFile()
↓
Open()
↓
Read()Similarly:
WriteFile()
↓
OpenFile()
↓
Write()The high-level API is convenient for common tasks.
The low-level API provides more control.
Common Beginner Mistakes
Forgetting Error Handling
Incorrect:
data, _ := os.ReadFile(...)During learning, always check errors.
Forgetting []byte
Incorrect:
os.WriteFile(
"file.txt",
"Hello",
0644,
)Correct:
os.WriteFile(
"file.txt",
[]byte("Hello"),
0644,
)Forgetting string()
Incorrect:
fmt.Println(data)Output:
[72 101 108 108 111]Correct:
fmt.Println(string(data))Using ReadFile for Very Large Files
For small files:
os.ReadFile()For large files:
bufio.ScannerChoose the API appropriate for the size of the data.
Comparison with C++
| Go | C++ |
|---|---|
os.ReadFile() | std::ifstream + read all |
os.WriteFile() | std::ofstream |
[]byte | std::vector<uint8_t> |
string(data) | Construct std::string from bytes |
defer file.Close() | RAII cleanup |
Session Summary
Topics covered:
-
Reading files
-
Writing files
-
[]byte -
File permissions
-
High-level file APIs
-
Low-level file APIs
-
Streaming large files
-
Current working directory
Practical skills gained:
-
Reading text files
-
Writing text files
-
Converting between bytes and strings
-
Choosing between convenience and streaming APIs
-
Understanding resource ownership
Key Takeaways
-
Files are sequences of bytes.
-
os.ReadFile()returns[]byte, notstring. -
os.ReadFile()opens, reads, and closes the file internally. -
Use
string(data)only when the file actually contains text. -
Use
os.ReadFile()for small files. -
Use
os.Open()withbufio.Scannerfor large files. -
Go often provides both a simple API and a more flexible low-level API.
Mental Model
Small file
↓
ReadFile()
Large file
↓
Open()
↓
Scanner
↓
Read → Process → Discard → RepeatNext Topics
- JSON
- Struct tags
- Encoding
- Decoding
- Saving and loading structured data
- Go Learning - Session 10