Contents
File Handling
Reading Files
1. Reading an Entire File into Memory
The simplest file operation is reading an entire file into memory using the os.ReadFile
function. Below is an example.
Directory Structure
├── Workspace
│ └── filedemo
│ ├── main.go
│ ├── go.mod
│ └── sample.txt
Content of sample.txt
:
Welcome to Go file handling!
Code in main.go
:
package main
import (
"fmt"
"os"
)
func main() {
content, err := os.ReadFile("sample.txt")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println("File content:", string(content))
}
Run Instructions:
cd ~/Workspace/filedemo/
go install
filedemo
Output:
File content: Welcome to Go file handling!
If you run the program from a different directory, you’ll encounter:
Error reading file: open sample.txt: no such file or directory
2. Using an Absolute File Path
Using an absolute path ensures the program works regardless of the current directory.
Updated Code in main.go
:
package main
import (
"fmt"
"os"
)
func main() {
content, err := os.ReadFile("/Users/user/Workspace/filedemo/sample.txt")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println("File content:", string(content))
}
Output:
File content: Welcome to Go file handling!
3. Passing the File Path as a Command-Line Argument
Using the flag
package, we can pass the file path dynamically.
Code in main.go
:
package main
import (
"flag"
"fmt"
"os"
)
func main() {
filePath := flag.String("file", "sample.txt", "Path of the file to read")
flag.Parse()
content, err := os.ReadFile(*filePath)
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println("File content:", string(content))
}
Run Instructions:
filedemo --file=/path/to/sample.txt
Output:
File content: Welcome to Go file handling!
4. Bundling the File within the Binary
Using the embed
package, we can include the file contents directly in the binary.
Code in main.go
:
package main
import (
_ "embed"
"fmt"
)
//go:embed sample.txt
var fileData []byte
func main() {
fmt.Println("File content:", string(fileData))
}
Compile and run the binary:
cd ~/Workspace/filedemo/
go install
filedemo
Output:
File content: Welcome to Go file handling!
5. Reading a File in Small Chunks
For large files, read them in chunks using the bufio
package.
Code in main.go
:
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
func main() {
filePath := flag.String("file", "sample.txt", "Path of the file to read")
flag.Parse()
file, err := os.Open(*filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
buffer := make([]byte, 5)
for {
bytesRead, err := reader.Read(buffer)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Print(string(buffer[:bytesRead]))
}
fmt.Println("\nFile read completed.")
}
6. Reading a File Line by Line
To process large files line by line, use a bufio.Scanner
.
Code in main.go
:
package main
import (
"bufio"
"flag"
"fmt"
"os"
)
func main() {
filePath := flag.String("file", "sample.txt", "Path of the file to read")
flag.Parse()
file, err := os.Open(*filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", err)
}
}
Run Instructions:
filedemo --file=/path/to/sample.txt
Output:
Welcome to Go file handling!
Writing Files using Go
One of the simplest and most common operations is writing a string to a file. The process includes the following steps:
- Create a file.
- Write the string to the file.
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("example.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
length, err := file.WriteString("Greetings, Universe!")
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
fmt.Println(length, "characters successfully written.")
}
This program creates a file named example.txt
. If it already exists, it will be overwritten. The WriteString
method writes the string to the file and returns the number of characters written along with any errors. On running the code, you’ll see:
21 characters successfully written.
Writing Bytes to a File
Writing raw bytes is similar to writing strings. The Write
method is used for this purpose.
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("output_bytes")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
data := []byte{72, 101, 108, 108, 111, 32, 98, 121, 116, 101, 115}
bytesWritten, err := file.Write(data)
if err != nil {
fmt.Println("Error writing bytes:", err)
return
}
fmt.Println(bytesWritten, "bytes successfully written.")
}
This code creates a file output_bytes
, writes a slice of bytes corresponding to the string Hello bytes
, and outputs the number of bytes written. Expected output:
11 bytes successfully written.
Writing Lines to a File
Often, we need to write multiple lines to a file. The Fprintln
function makes this straightforward:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("multi_lines.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
lines := []string{
"Go is fun to learn.",
"It is concise and efficient.",
"File handling is straightforward.",
}
for _, line := range lines {
if _, err := fmt.Fprintln(file, line); err != nil {
fmt.Println("Error writing line:", err)
return
}
}
fmt.Println("Lines written successfully.")
}
This will create a file named multi_lines.txt
containing:
Go is fun to learn.
It is concise and efficient.
File handling is straightforward.
Appending to a File
To add content to an existing file, open it in append mode:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("multi_lines.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
newLine := "Appending is simple!"
if _, err := fmt.Fprintln(file, newLine); err != nil {
fmt.Println("Error appending to file:", err)
return
}
fmt.Println("Line appended successfully.")
}
This program appends a new line to multi_lines.txt
, resulting in:
Go is fun to learn.
It is concise and efficient.
File handling is straightforward.
Appending is simple!
Concurrent File Writing
When multiple goroutines write to a file, coordination is necessary to avoid race conditions. This can be achieved using channels.
Here’s an example that generates 50 random numbers concurrently and writes them to a file:
package main
import (
"fmt"
"math/rand"
"os"
"sync"
)
func generateNumbers(data chan int, wg *sync.WaitGroup) {
num := rand.Intn(1000)
data <- num
wg.Done()
}
func writeToFile(data chan int, done chan bool) {
file, err := os.Create("random_numbers.txt")
if err != nil {
fmt.Println("Error creating file:", err)
done <- false
return
}
defer file.Close()
for num := range data {
if _, err := fmt.Fprintln(file, num); err != nil {
fmt.Println("Error writing to file:", err)
done <- false
return
}
}
done <- true
}
func main() {
dataChannel := make(chan int)
doneChannel := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go generateNumbers(dataChannel, &wg)
}
go writeToFile(dataChannel, doneChannel)
go func() {
wg.Wait()
close(dataChannel)
}()
if <-doneChannel {
fmt.Println("Random numbers written successfully.")
} else {
fmt.Println("Failed to write random numbers.")
}
}
This program creates a file random_numbers.txt
containing 50 randomly generated numbers.