1. Cơ bản về Thư viện OS

Gói os trong Golang cung cấp một giao diện độc lập với nền tảng cho các chức năng của hệ điều hành. Tiếp theo, chúng ta sẽ thảo luận về cách sử dụng gói os để xử lý mở, đóng, đọc, ghi tập tin cũng như lấy và thiết lập thuộc tính của tập tin.

1.1 Mở và Đóng Tập Tin

Trong ngôn ngữ Go, bạn có thể sử dụng hàm os.Open để mở một tập tin, nó sẽ trả về một đối tượng *os.File và một lỗi. Sau khi tập tin được mở, bạn có thể thực hiện các hoạt động đọc, ghi và các hoạt động khác. Sau khi các hoạt động hoàn tất, bạn nên gọi file.Close để đóng tập tin và giải phóng tài nguyên tương ứng.

Dưới đây là một ví dụ về việc mở một tập tin:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Mở tập tin test.txt trong thư mục hiện tại
    file, err := os.Open("test.txt")
    if err != nil {
        // Xử lý lỗi khi mở tập tin
        fmt.Println("Lỗi khi mở tập tin:", err)
        return
    }
    // Sử dụng câu lệnh defer để đảm bảo tập tin sẽ được đóng cuối cùng
    defer file.Close()

    // Các hoạt động xử lý tập tin...

    fmt.Println("Tập tin mở thành công")
}

Trong đoạn mã trên, chúng ta sử dụng câu lệnh defer để đảm bảo rằng file.Close sẽ được thực thi bất kể thế nào. Điều này là một thực hành phổ biến trong ngôn ngữ Go để dọn dẹp tài nguyên.

1.2 Các Hoạt Động Đọc và Ghi Tập Tin

Kiểu os.File có các phương thức ReadWrite, có thể được sử dụng cho các hoạt động đọc và ghi tập tin. Phương thức Read đọc dữ liệu từ tập tin vào mảng byte, và phương thức Write ghi dữ liệu từ mảng byte vào tập tin.

Ví dụ sau minh họa cách đọc và ghi vào tập tin:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Mở tập tin
    file, err := os.OpenFile("test.txt", os.O_RDWR, 0644)
    if err != nil {
        fmt.Println("Lỗi khi mở tập tin:", err)
        return
    }
    defer file.Close()

    // Ghi nội dung vào tập tin
    message := []byte("Xin chào, Gophers!")
    _, writeErr := file.Write(message)
    if writeErr != nil {
        fmt.Println("Lỗi khi ghi vào tập tin:", writeErr)
        return
    }

    // Đọc tập tin từ đầu
    file.Seek(0, 0)
    buffer := make([]byte, len(message))
    _, readErr := file.Read(buffer)
    if readErr != nil {
        fmt.Println("Lỗi khi đọc tập tin:", readErr)
        return
    }

    fmt.Println("Nội dung tập tin:", string(buffer))
}

Trong ví dụ này, chúng ta sử dụng os.OpenFile thay vì os.Open. Hàm os.OpenFile cho phép bạn chỉ định chế độ và quyền khi mở tập tin. Trong ví dụ trên, chúng ta sử dụng cờ os.O_RDWR, có nghĩa là tập tin sẽ được mở ở chế độ đọc ghi.

1.3 Thuộc tính và Quyền truy cập File

Bạn có thể sử dụng các hàm từ gói os để truy cập và sửa đổi thông tin trạng thái của file. Sử dụng os.Stat hoặc os.Lstat để có được giao diện os.FileInfo, cung cấp thông tin về file như kích thước, quyền truy cập, thời gian sửa đổi, và nhiều hơn nữa.

Dưới đây là một ví dụ về cách nhận thông tin trạng thái file:

package main

import (
	"fmt"
	"os"
)

func main() {
	fileInfo, err := os.Stat("test.txt")
	if err != nil {
		fmt.Println("Lỗi khi lấy thông tin file:", err)
		return
	}

	// In kích thước file
	fmt.Printf("Kích thước file: %d bytes\n", fileInfo.Size())

	// In quyền truy cập file
	fmt.Printf("Quyền truy cập file: %s\n", fileInfo.Mode())
}

Nếu bạn cần đổi tên file hoặc sửa đổi quyền truy cập của file, bạn có thể sử dụng os.Rename để đổi tên file hoặc os.Chmod để thay đổi quyền truy cập của file.

package main

import (
	"fmt"
	"os"
)

func main() {
	// Thay đổi quyền truy cập của file thành chỉ đọc
	err := os.Chmod("test.txt", 0444)
	if err != nil {
		fmt.Println("Lỗi khi thay đổi quyền truy cập file:", err)
		return
	}

	// Đổi tên file
	renameErr := os.Rename("test.txt", "renamed.txt")
	if renameErr != nil {
		fmt.Println("Lỗi khi đổi tên file:", renameErr)
		return
	}

	fmt.Println("Các thao tác file thành công")
}

Ở đây, chúng ta đổi quyền truy cập của file test.txt thành chỉ đọc, sau đó đổi tên file thành renamed.txt. Lưu ý rằng khi sửa đổi quyền truy cập file, cẩn thận vì cài đặt quyền truy cập không đúng có thể dẫn đến file không thể truy cập.

2. Cách sử dụng cơ bản của Thư viện IO

Trong ngôn ngữ Go, thư viện io cung cấp các giao diện cơ bản cho các nguyên tắc I/O (các hoạt động nhập/ xuất). Thiết kế của thư viện io tuân theo các nguyên tắc đơn giản và giao diện đồng nhất, cung cấp hỗ trợ cơ bản cho các loại hoạt động I/O khác nhau, như đọc/ghi file, giao tiếp mạng, đệm dữ liệu, và nhiều hơn nữa.

2.2 Sử dụng Giao diện Đọc (Reader) và Ghi (Writer)

io.Readerio.Writer là hai giao diện cơ bản được sử dụng để chỉ định các hoạt động đọc và ghi của một đối tượng. Chúng được thực hiện bởi các loại khác nhau, như file, kết nối mạng và bộ đệm.

io.Reader

Giao diện io.Reader có một phương thức Read:

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

Phương thức này đọc tối đa len(p) byte dữ liệu từ io.Reader vào p. Nó trả về số byte đọc được n (0 <= n <= len(p)) và bất kỳ lỗi nào gặp phải.

Đoạn mã mẫu:

package main

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

func main() {
	r := strings.NewReader("Xin chào, Thế giới!")

	buf := make([]byte, 4)
	for {
		n, err := r.Read(buf)
		if err == io.EOF {
			break
		}
		fmt.Printf("Số byte đọc: %d, Nội dung: %s\n", n, buf[:n])
	}
}

Trong ví dụ này, chúng ta tạo một strings.NewReader để đọc dữ liệu từ một chuỗi sau đó đọc dữ liệu theo từng khối 4 byte.

io.Writer

Giao diện io.Writer có phương thức Write:

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

Phương thức này ghi dữ liệu từ p vào luồng dữ liệu cơ bản, trả về số byte được ghi và bất kỳ lỗi nào gặp phải.

Đoạn mã mẫu:

package main

import (
	"fmt"
	"os"
)

func main() {
	data := []byte("Xin chào, Thế giới!\n")
	n, err := os.Stdout.Write(data)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Số byte đã ghi: %d\n", n)
}

Ví dụ này ghi một chuỗi đơn giản vào đầu ra chuẩn os.Stdout, đóng vai trò là một triển khai của io.Writer.

2.3 Các Chức Năng Đọc/Viết Nâng Cao

Gói io cung cấp một số chức năng nâng cao có thể giúp đơn giản hóa các nhiệm vụ thông thường, như việc sao chép dữ liệu và đọc một lượng dữ liệu cụ thể.

Chức Năng Sao Chép

io.Copy là một phương pháp tiện lợi để sao chép trực tiếp dữ liệu từ một io.Reader sang một io.Writer mà không cần một bộ đệm trung gian.

Mã mẫu:

package main

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

func main() {
    r := strings.NewReader("Ví dụ về thao tác sao chép đơn giản")
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        panic(err)
    }
}

Trong ví dụ này, chúng ta sao chép trực tiếp một chuỗi vào đầu ra tiêu chuẩn.

Chức Năng ReadAtLeast

Chức năng io.ReadAtLeast được sử dụng để đảm bảo rằng ít nhất một lượng dữ liệu cụ thể được đọc từ một io.Reader trước khi trả về.

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

Mã mẫu:

package main

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

func main() {
    r := strings.NewReader("Trang web Tiếng Trung về Ngôn Ngữ Go")
    buf := make([]byte, 14)
    n, err := io.ReadAtLeast(r, buf, 14)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%s\n", buf[:n])
}

Trong ví dụ này, io.ReadAtLeast cố gắng đọc ít nhất 14 byte dữ liệu vào buf.

Các chức năng đọc/viết nâng cao này cho phép bạn xử lý các nhiệm vụ liên quan đến I/O một cách hiệu quả và cung cấp một nền tảng vững chắc để xây dựng logic chương trình phức tạp hơn.