1. Basics of the OS Library

The os package in Golang provides a platform-independent interface for operating system functions. Next, we will discuss how to use the os package to handle file opening, closing, reading, writing, as well as obtaining and setting file attributes.

1.1 Opening and Closing Files

In Go language, you can use the os.Open function to open a file, which will return a *os.File object and an error. Once the file is opened, you can perform read, write, and other operations. After the operations are completed, you should call file.Close to close the file and release the corresponding resources.

Here's an example of opening a file:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Open the file test.txt in the current directory
    file, err := os.Open("test.txt")
    if err != nil {
        // Handle file opening error
        fmt.Println("Error opening file:", err)
        return
    }
    // Use defer statement to ensure the file is closed eventually
    defer file.Close()

    // File handling operations...

    fmt.Println("File opened successfully")
}

In the above code, we use the defer statement to ensure that file.Close will be executed regardless. This is a common practice in Go language for resource cleanup.

1.2 File Read and Write Operations

The os.File type has Read and Write methods, which can be used for file read and write operations. The Read method reads data from the file into a byte slice, and the Write method writes data from a byte slice to the file.

The following example demonstrates how to read from and write to a file:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Open the file
    file, err := os.OpenFile("test.txt", os.O_RDWR, 0644)
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    defer file.Close()

    // Write file content
    message := []byte("Hello, Gophers!")
    _, writeErr := file.Write(message)
    if writeErr != nil {
        fmt.Println("Error writing to file:", writeErr)
        return
    }

    // Read the file from the beginning
    file.Seek(0, 0)
    buffer := make([]byte, len(message))
    _, readErr := file.Read(buffer)
    if readErr != nil {
        fmt.Println("Error reading file:", readErr)
        return
    }

    fmt.Println("File content:", string(buffer))
}

In this example, we use os.OpenFile instead of os.Open. The os.OpenFile function allows you to specify the mode and permissions to be used when opening the file. In the example above, the os.O_RDWR flag is used, meaning the file will be opened in read-write mode.

1.3 File Properties and Permissions

You can use functions from the os package to access and modify file status information. Use os.Stat or os.Lstat to obtain the os.FileInfo interface, which provides information about the file, such as size, permissions, modification time, and more.

Here's an example of how to obtain file status:

package main

import (
    "fmt"
    "os"
)

func main() {
    fileInfo, err := os.Stat("test.txt")
    if err != nil {
        fmt.Println("Error getting file information:", err)
        return
    }

    // Print file size
    fmt.Printf("File size: %d bytes\n", fileInfo.Size())

    // Print file permissions
    fmt.Printf("File permissions: %s\n", fileInfo.Mode())
}

If you need to change the file name or modify the file's permissions, you can use os.Rename to rename the file or os.Chmod to change the file's permissions.

package main

import (
    "fmt"
    "os"
)

func main() {
    // Change file permissions to read-only
    err := os.Chmod("test.txt", 0444)
    if err != nil {
        fmt.Println("Error changing file permissions:", err)
        return
    }

    // Rename the file
    renameErr := os.Rename("test.txt", "renamed.txt")
    if renameErr != nil {
        fmt.Println("Error renaming file:", renameErr)
        return
    }
    
    fmt.Println("File operations successful")
}

Here, we change the permissions of the test.txt file to read-only, and then rename the file to renamed.txt. Note that when modifying file permissions, be cautious, as incorrect permissions settings may lead to inaccessible files.

2. Basic Usage of the IO Library

In the Go language, the io library provides basic interfaces for I/O primitives (input/output operations). The design of the io library follows the principles of simplicity and uniform interfaces, providing basic support for different types of I/O operations, such as file read/write, network communication, data buffering, and more.

2.2 Using the Reader and Writer Interfaces

io.Reader and io.Writer are two basic interfaces used to specify the read and write operations of an object. They are implemented by different types, such as files, network connections, and buffers.

io.Reader

The io.Reader interface has a Read method:

Read(p []byte) (n int, err error)

This method reads up to len(p) bytes of data from the io.Reader into p. It returns the number of bytes read n (0 <= n <= len(p)) and any error encountered.

Sample code:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, World!")

    buf := make([]byte, 4)
    for {
        n, err := r.Read(buf)
        if err == io.EOF {
            break
        }
        fmt.Printf("Bytes read: %d, Content: %s\n", n, buf[:n])
    }
}

In this example, we create a strings.NewReader to read data from a string and then read the data in 4-byte chunks.

io.Writer

The io.Writer interface has a Write method:

Write(p []byte) (n int, err error)

This method writes the data from p into the underlying data stream, returning the number of bytes written and any error encountered.

Sample code:

package main

import (
    "fmt"
    "os"
)

func main() {
    data := []byte("Hello, World!\n")
    n, err := os.Stdout.Write(data)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Bytes written: %d\n", n)
}

This example writes a simple string to the standard output os.Stdout, which acts as an implementation of io.Writer.

2.3 Advanced Read/Write Functions

The io package provides some advanced functions that can simplify common tasks, such as copying data and reading a specified amount of data.

Copy Function

io.Copy is a convenient method for directly copying data from an io.Reader to an io.Writer without needing an intermediate buffer.

Sample code:

package main

import (
    "io"
    "os"
    "strings"
)

func main() {
    r := strings.NewReader("Simple copy operation example")
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        panic(err)
    }
}

In this example, we directly copy a string to the standard output.

ReadAtLeast Function

The io.ReadAtLeast function is used to ensure that at least a specified amount of data is read from an io.Reader before returning.

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

Sample code:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Go Language Chinese Website")
    buf := make([]byte, 14)
    n, err := io.ReadAtLeast(r, buf, 14)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%s\n", buf[:n])
}

In this example, io.ReadAtLeast attempts to read at least 14 bytes of data into buf.

These advanced read/write functions allow you to handle I/O-related tasks more efficiently and provide a solid foundation for building more complex program logic.