Saturday, February 16, 2019

File Handling in Go - Part 1

Go language provides os package for using operating system services and that includes file handling.

Let's go through a step by step process and learn how to open files and read and write to them in Go Lang.

Opening a file

Go provides two function calls, Open() and OpenFile(), to open a file. Here is the function declaration of these two:-

func Open(name string) (*File, error)

func OpenFile(name string, flag int, perm FileMode) (*File, error) 

Open() takes the name of the file to open as an argument and returns a file handle ( or object ) and error if there is any.The thing we have to keep in mind is that when we open a file using Open() function call, it opens in a read-only mode. So you can only read from the file and not write to it. To be able to write to a file, we would have to use the OpenFile() function.

To start with let's use the Open() call to get a hang of it. Here's the program for it.

package main
import (
    "fmt"
    "os"
)

func main() {

    filehandle,err := os.Open("hello.txt")
    if err == nil {
        fmt.Println("File Opened successfully", filehandle)
    } else {
        fmt.Println("Error :", err)
    }
}

So, I created a file named hello.txt in the same directory as my Go program and then ran this program. In the above program, we import the package os that provides us with OS interface. We call the Open() function and check for error. If there is no error, we print the file handle, which is basically some number.

Reading from a file

So that was a pretty basic example. Now let's try to read something from the opened file. For that we will use the following function call provided by os package.

 func (f *File) Read(b []byte) (n int, err error)


Read() function takes as an input an array of bytes, b, into which it will fill whatever it reads from the file. It returns the number of bytes it read from the file, n, and error if there was any.

Let's enhance the above program to read something from the file, hello.txt.

package main
import (
    "fmt"
    "os"
)

func main() {

    filehandle,err := os.Open("hello.txt")
    b := make([]byte, 20)

    if err == nil {
        fmt.Println("File Opened successfully", filehandle)

        n, err := filehandle.Read(b)

        if err == nil {
            fmt.Printf("Read Success. N=[%d], Text=[%s]", n, b)
        } else {
            fmt.Println("Read Error:",err)
        }

    } else {
        fmt.Println("Error :", err)
    }
}


So we added a byte array, b, that is allocated a space of 20 bytes. We called Read() function using the file handle that we received when we opened the file using Open(), and then we printed whatever we read from the file in case there was no error.

Writing to a file

So that was pretty simple, I hope. Opening and Reading from a file in Go is quite straightforward. Now, let's explore how to write to a file.

Remember that Open() function opens a file in read-only mode. To be able to write to a file, we need to use OpenFile() function. First, let's understand how to use the OpenFile() function.

func OpenFile(name string, flag int, perm FileMode) (*File, error)

The first argument of the OpenFile() is the name of the file that we would like to open.
Next is an integer called flag. flag can take one of the following values.

const (
 // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
 O_RDONLY int = syscall.O_RDONLY // open the file read-only.
 O_WRONLY int = syscall.O_WRONLY // open the file write-only.
 O_RDWR   int = syscall.O_RDWR   // open the file read-write.
 // The remaining values may be or'ed in to control behavior.
 O_APPEND int = syscall.O_APPEND //append to the file when writing.
 O_CREATE int = syscall.O_CREAT  //createnew file if none exists.
 O_EXCL   int = syscall.O_EXCL //used with O_CREATE,file must not exist.
 O_SYNC   int = syscall.O_SYNC   //open for synchronous I/O.
 O_TRUNC  int = syscall.O_TRUNC  //truncate file when opened.
)


So, if we would open a file for read-only purpose, the value of the flag will be O_RDONLY. If we open a file for only writing, then flag shall be O_WRONLY and so on.

As documentation above suggests, flag can only have either one of the following values O_RDONLY, O_WRONLY, O_RDWR. The rest of the values in the above block can be ORed with any of the first three, depending on the requirement.

For example,
If we wanted to open the file for Reading and writing, and we wanted that when we write to the file, the data should be appended to it, we would have the value of flag as
O_RDWR | O_APPEND. 
And if we wanted to want to open a file for writing and if the file does not exist we would like have it created, the value of the flag would be
O_WRONLY | O_CREATE
  

Next Argument, perm, is of type FileMode. This argument defines the kind of permissions that this file has and what kind of file it is. It is a 32 bit integer, where the least significant 9 bits, represent the standard unix permissions like rwxrwxrwx. So, while creating the file you can specify the kind of permissions that this file is going to be created with.

The Other bits in the FileMode have their own significance, but we will not cover them in this lesson.

So, now that we have understood each argument of the FileOpen() function, now let's write a short program, that will write some text to a file.

To write to a file, we shall use the Write() function.

func (f *File) Write(b []byte) (n int, err error)

The Write() function, takes a byte array as an argument and writes the contents of this byte array to the file. It returns the number of bytes written to file and error if there is any.

Here's the program that depicts usage of FileOpen() and Write().

package main
import (
    "fmt"
    "os"
)

func main() {

    f, err := os.OpenFile("gotest.txt",os.O_RDWR|os.O_CREATE,0777)

    if err == nil {

        fmt.Println("File Opened for writing successfully")

        b := []byte("Hello there")

        n,err := f.Write(b)

        if err == nil {
            fmt.Printf("Written %d bytes successfully",n)
        } else {
            fmt.Println("Error in Writing to file",err)
        }

    } else {

        fmt.Println("Error in Opening file", err)

    }
}

In the above program, we opened a file "gotest.txt" with flag O_RDWR | O_CREATE and file permission as 0777. We wrote a byte array containing the text "Hello there" to the file. We check for error as always before declaring success.

Well, that is it for this lesson. We shall see other File Handling related functions provided by os package in the next lesson.

Part 2 of this lesson is now available.

References:

No comments:

Post a Comment