Go Files
Links: 103 Golang Index
- We use the package
os
to work with files- It provides platform independent interface
- It is intended to be uniform across all OSes which means the programs we create work the same on Linux, Windows and Mac.
log.Fatal
should be used for printing errors since its thread safe and also prints out the timing information- It will also exit the program with a status code of 1
Creating, Renaming, Deleting etc¶
/////////////////////////////////
// Creating, Opening, Closing, Renaming, Moving, and Removing files in Go
/////////////////////////////////
package main
import (
"fmt"
"log"
"os"
)
func main() {
//** Use valid paths according to your OS. **//
// CREATING A FILE
// os.Create() function creates a file if it doesn't already exist. If it exists, the file is truncated.
// it returns a file descriptor which is a pointer to os.File and an error value.
newFile, err := os.Create("a.txt")
// error handling
if err != nil {
// log the error and exit the program
log.Fatal(err) // the idiomatic way to handle errors
}
// TRUNCATING A FILE
err = os.Truncate("a.txt", 0) //0 means completely empty the file.
// error handling
if err != nil {
log.Fatal(err)
}
// CLOSING THE FILE
newFile.Close()
// OPEN AND CLOSE AN EXISTING FILE
file, err := os.Open("a.txt") // open in read-only mode
// error handling
if err != nil {
log.Fatal(err)
}
file.Close()
//OPENING a FILE WITH MORE OPTIONS
file, err = os.OpenFile("a.txt", os.O_APPEND, 0644)
// We can Use opening attributes individually or combined
// using an OR between them
// e.g. os.O_CREATE|os.O_APPEND
// or os.O_CREATE|os.O_TRUNC|os.O_WRONLY
// os.O_RDONLY // Read only
// os.O_WRONLY // Write only
// os.O_RDWR // Read and write
// os.O_APPEND // Append to end of file
// os.O_CREATE // Create is none exist
// os.O_TRUNC // Truncate file when opening
// error handling
if err != nil {
log.Fatal(err)
}
file.Close()
// GETTING FILE INFO
var fileInfo os.FileInfo
fileInfo, err = os.Stat("a.txt")
p := fmt.Println
p("File Name:", fileInfo.Name()) // => File Name: a.txt
p("Size in bytes:", fileInfo.Size()) // => Size in bytes: 0
p("Last modified:", fileInfo.ModTime()) // => Last modified: 2019-10-21 16:16:00.325037748 +0300 EEST
p("Is Directory? ", fileInfo.IsDir()) // => Is Directory? false
p("Pemissions:", fileInfo.Mode()) // => Pemissions: -rw-r-----
// CHECKING IF FILE EXISTS
fileInfo, err = os.Stat("b.txt")
// error handling
if err != nil {
if os.IsNotExist(err) {
log.Fatal("The file does not exist")
}
}
// RENAMING AND MOVING A FILE
oldPath := "a.txt"
newPath := "aaa.txt"
err = os.Rename(oldPath, newPath)
// error handling
if err != nil {
log.Fatal(err)
}
// REMOVING A FILE
err = os.Remove("aa.txt")
// error handling
if err != nil {
log.Fatal(err)
}
}
Writing to a file¶
- For writing a string to file we convert it to a byte slice or rune slice if it contains non ASCII character.
Using os and ioutil¶
/////////////////////////////////
// Writing Bytes to Files
/////////////////////////////////
package main
import (
"io/ioutil"
"log"
"os"
)
func main() {
// opening the file in write-only mode if the file exists and then it truncates the file.
// if the file doesn't exist it creates the file with 0644 permissions
file, err := os.OpenFile(
"b.txt",
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0644,
)
// error handling
if err != nil {
log.Fatal(err)
}
// defer closing the file
defer file.Close()
// WRITING BYTES TO FILE
byteSlice := []byte("I learn Golang! δΌ ") // converting a string to a bytes slice
bytesWritten, err := file.Write(byteSlice) // writing bytes to file.
// It returns the no. of bytes written and an error value
// error handling
if err != nil {
log.Fatal(err)
}
log.Printf("Bytes written: %d\n", bytesWritten) // => 2019/10/21 16:26:16 Bytes written: 19
// WRITING BYTES TO FILE USING ioutil.WriteFile()
// ioutil.WriteFile() handles creating, opening, writing a slice of bytes and closing the file.
// if the file doesn't exist WriteFile() creates it
// and if it already exists the function will truncate it before writing to file.
bs := []byte("Go Programming is cool!")
err = ioutil.WriteFile("c.txt", bs, 0644)
// error handling
if err != nil {
log.Fatal(err)
}
}
Using buffio (buffered writer)¶
- It lets us create a buffered writer so that we can write to buffer in memory before writing it to disk.
- This is specially useful if we need to do lot of manipulations on the data before writing it to disk.
- We can save a lot of disk io time
- It is also useful if we want to write to a file byte by byte.
- We store a large number of bytes in memory before writing it to the file.
- This is faster since disk operations are slow.
- The default size of buffer is 4096 bytes
/////////////////////////////////
// Writing to Files using a Buffer in Memory
//
/////////////////////////////////
package main
import (
"bufio"
"log"
"os"
)
func main() {
// Opening the file for writing
file, err := os.OpenFile("my_file.txt", os.O_WRONLY|os.O_CREATE, 0644)
// error handling
if err != nil {
log.Fatal(err)
}
// defer closing the file
defer file.Close()
// Creating a buffered writer from the file variable using bufio.NewWriter()
bufferedWriter := bufio.NewWriter(file)
// declaring a byte slice
bs := []byte{97, 98, 99}
// writing the byte slice to the buffer in memory
bytesWritten, err := bufferedWriter.Write(bs)
// error handling
if err != nil {
log.Fatal(err)
}
log.Printf("Bytes written to buffer (not file): %d\n", bytesWritten)
// => 2019/10/21 16:30:59 Bytes written to buffer (not file): 3
// checking the available buffer
bytesAvailable := bufferedWriter.Available()
log.Printf("Bytes available in buffer: %d\n", bytesAvailable)
// => 2019/10/21 16:30:59 Bytes available in buffer: 4093
// writing a string (not a byte slice) to the buffer in memory
bytesWritten, err = bufferedWriter.WriteString("\nJust a random string")
// error handling
if err != nil {
log.Fatal(err)
}
// checking how much data is stored in buffer, just waiting to be written to disk
unflushedBufferSize := bufferedWriter.Buffered()
log.Printf("Bytes buffered: %d\n", unflushedBufferSize)
// -> 24 (3 bytes in the byte slice + 21 runes in the string, each rune is 1 byte)
// The bytes have been written to buffer, not yet to file.
// Writing from buffer to file.
bufferedWriter.Flush()
}
Reading from a file¶
Using io and ioutil¶
/////////////////////////////////
// Reading Files in Go
/////////////////////////////////
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
)
func main() {
//** READING INTO A BYTE SLICE USING io.ReadFull() **//
// Opening the file in read-only mode. The file must exist (in the current working directory)
// Use a valid path!
file, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// declaring a byte slice and initializing it with a length of 2
byteSlice := make([]byte, 2)
// io.ReadFull() returns an error if the file is smaller than the byte slice.
// it reads the file into the byte slice up to its length
numberBytesRead, err := io.ReadFull(file, byteSlice)
if err != nil {
log.Fatal(err)
}
log.Printf("Number of bytes read: %d\n", numberBytesRead)
log.Printf("Data read: %s\n", byteSlice)
fmt.Println(strings.Repeat("#", 20))
//** READING WHOLE FILE INTO A BYTESLICE USING ioutil.ReadAll() **//
// Opening another file (from the current working directory)
file, err = os.Open("main.go")
if err != nil {
log.Fatal(err)
}
// ioutil.ReadAll() reads every byte from the file and return a slice of unknown size
data, err := ioutil.ReadAll(file)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Data as string: %s\n", data)
fmt.Println("Number of bytes read:", len(data))
//** READING WHOLE FILE INTO MEMORY USING ioutil.ReadFile() **//
// ioutil.ReadFile() reads a file into byte slice
// this function handles opening and closing the file.
data, err = ioutil.ReadFile("test.txt")
if err != nil {
log.Fatal(err)
}
log.Printf("Data read: %s\n", data)
}
Using buffio¶
- Reading line by line separated by a delimiter
///////////////////////////////// // Reading Files Line by Line (or using a delimiter) using bufio.Scanner // Go Playground: https://play.golang.org/p/O3szMQZ7QZX ///////////////////////////////// package main import ( "bufio" "fmt" "log" "os" ) func main() { // opening the file in read-only mode. The file must exist (in the current working directory) // use a valid path! file, err := os.Open("my_file.txt") // error handling if err != nil { log.Fatal(err) } // defer closing the file defer file.Close() // the file value returned by os.Open() is wrapped in a bufio.Scanner just like a buffered reader. scanner := bufio.NewScanner(file) // the default scanner is bufio.ScanLines and that means it will scan a file line by line. // there are also bufio.ScanWords and bufio.ScanRunes. // scanner.Split(bufio.ScanLines) // scanning for next token in this case \n which is the line delimiter. success := scanner.Scan() //read a line if success == false { // false on error or EOF. Check for errors err = scanner.Err() if err == nil { log.Println("Scan was completed and it reached End Of File.") } else { log.Fatal(err) } } // Getting the data from the scanner with Bytes() or Text() fmt.Println("First Line found:", scanner.Text()) //If we want the next token, so the next line or \n, we call scanner.Scan() again // Reading the whole remaining part of the file: for scanner.Scan() { fmt.Println(scanner.Text()) } // Checking for any possible errors: if err := scanner.Err(); err != nil { log.Fatal(err) } }
Reading from stdin (console)¶
/////////////////////////////////
// Reading From Standard Input (console)
/////////////////////////////////
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
// creating a scanner
scanner := bufio.NewScanner(os.Stdin) //os.Stdin reads the output from the command line
fmt.Printf("%T\n", scanner) //pointer to bufio.scanner
scanner.Scan() //it waits for the input and buffers the input untill a new line
// gettting the scanned data
text := scanner.Text() // string type
bytes := scanner.Bytes() // uint8[] slice type
fmt.Println("Input text:", text)
fmt.Println("Input bytes:", bytes)
// reading the input continously until a specific string is scanned
for scanner.Scan() {
text = scanner.Text()
fmt.Println("You entered:", text)
if text == "exit" {
fmt.Println("Exiting the scanning ...")
break
}
}
// error handling
if err := scanner.Err(); err != nil {
log.Println(err)
}
}
Last updated: 2022-05-29