1. مبانی کتابخانه OS

بسته os در زبان گولانگ واسطی مستقل از پلتفرم برای انجام عملیات سیستم عامل فراهم می‌کند. در ادامه، به بحث درباره استفاده از بسته os برای انجام عملیات‌های باز کردن، بستن، خواندن، نوشتن فایل و همچنین به دست آوردن و تنظیم ویژگی‌های فایل خواهیم پرداخت.

1.1 باز کردن و بستن فایل‌ها

در زبان گولانگ، می‌توانید از تابع os.Open برای باز کردن یک فایل استفاده کنید که یک شیء *os.File و یک خطا برمی‌گرداند. پس از باز شدن فایل، می‌توانید عملیات خواندن، نوشتن و دیگر عملیات‌ها را انجام دهید. پس از انجام عملیات، باید file.Close را فراخوانی کنید تا فایل بسته شده و منابع مربوطه آزاد شود.

در زیر مثالی از باز کردن یک فایل آمده است:

package main

import (
    "fmt"
    "os"
)

func main() {
    // باز کردن فایل test.txt در دایرکتوری فعلی
    file, err := os.Open("test.txt")
    if err != nil {
        // رفع خطای باز کردن فایل
        fmt.Println("خطا در باز کردن فایل:", err)
        return
    }
    // استفاده از بیانه defer برای اطمینان از بستن فایل در نهایت
    defer file.Close()

    // عملیات‌های دستورات...

    fmt.Println("فایل با موفقیت باز شد")
}

در کد فوق، از بیانه defer برای اطمینان از اجرای file.Close در هر حالتی استفاده شده است. این یک شیوه رایج در زبان گولانگ برای پاکسازی منابع است.

1.2 عملیات خواندن و نوشتن فایل

نوع os.File دارای متدهای Read و Write است که برای عملیات خواندن و نوشتن فایل مورد استفاده قرار می‌گیرند. متد Read داده‌ها را از فایل به یک برش بایت می‌خواند و متد Write داده‌ها را از یک برش بایت به فایل می‌نویسد.

مثال زیر نحوه خواندن و نوشتن از یک فایل را نشان می‌دهد:

package main

import (
    "fmt"
    "os"
)

func main() {
    // باز کردن فایل
    file, err := os.OpenFile("test.txt", os.O_RDWR, 0644)
    if err != nil {
        fmt.Println("خطا در باز کردن فایل:", err)
        return
    }
    defer file.Close()

    // نوشتن محتوای فایل
    message := []byte("سلام، گوفرها!")
    _, writeErr := file.Write(message)
    if writeErr != nil {
        fmt.Println("خطا در نوشتن فایل:", writeErr)
        return
    }

    // خواندن فایل از ابتدا
    file.Seek(0, 0)
    buffer := make([]byte, len(message))
    _, readErr := file.Read(buffer)
    if readErr != nil {
        fmt.Println("خطا در خواندن فایل:", readErr)
        return
    }

    fmt.Println("محتوای فایل:", string(buffer))
}

در این مثال، از os.OpenFile به جای os.Open استفاده شده است. تابع os.OpenFile به شما امکان مشخص کردن حالت و دسترسی‌های استفاده شده برای باز کردن فایل را می‌دهد. در مثال بالا، از پرچم os.O_RDWR استفاده شده است که به معنای باز کردن فایل در حالت خواندن و نوشتن است.

۱.۳ ویژگی‌ها و مجوزهای فایل

شما می‌توانید از توابع بسته os برای دسترسی و تغییر اطلاعات وضعیت فایل استفاده کنید. از os.Stat یا os.Lstat برای به دست آوردن رابط os.FileInfo استفاده کنید که اطلاعاتی از همانند اندازه، مجوزها، زمان اصلاح و ... را ارائه می‌دهد.

اینجا یک نمونه از چگونگی به دست آوردن وضعیت فایل است:

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())
}

اگر نیاز به تغییر نام فایل یا تغییر مجوزهای فایل داشته باشید، می‌توانید از os.Rename برای تغییر نام فایل و از os.Chmod برای تغییر مجوزهای فایل استفاده کنید.

package main

import (
    "fmt"
    "os"
)

func main() {
    // تغییر دادن مجوزهای فایل به فقط‌خواندنی
    err := os.Chmod("test.txt", 0444)
    if err != nil {
        fmt.Println("Error changing file permissions:", err)
        return
    }

    // تغییر نام فایل
    renameErr := os.Rename("test.txt", "renamed.txt")
    if renameErr != nil {
        fmt.Println("Error renaming file:", renameErr)
        return
    }
    
    fmt.Println("File operations successful")
}

در اینجا، ما مجوزهای فایل test.txt را به حالت فقط‌خواندنی تغییر می‌دهیم و سپس فایل را به renamed.txt تغییر نام می‌دهیم. توجه کنید که هنگام تغییر مجوزهای فایل، باید محتاط باشید، زیرا تنظیمات نادرست مجوز ممکن است به فایل‌های قابل دسترسی ناممکن منجر شود.

۲. استفاده ابتدایی از کتابخانه IO

در زبان Go، کتابخانه io رابط‌های ابتدایی را برای ابزارهای ورودی/خروجی (عملیات ورودی/خروجی) فراهم می‌کند. طراحی کتابخانه‌ی io از اصول سادگی و رابط‌های یکنواخت پیروی می‌کند و پشتیبانی ابتدایی را برای انواع مختلف عملیات I/O مانند خواندن/نوشتن فایل، ارتباط شبکه، بافرهای داده و ... فراهم می‌کند.

۲.۲ استفاده از رابط‌های Reader و Writer

io.Reader و io.Writer دو رابط ابتدایی برای تعیین عملیات خواندن و نوشتن یک شی می‌باشند. آن‌ها توسط انواع مختلفی مانند فایل‌ها، اتصالات شبکه و بافرها پیاده‌سازی می‌شوند.

io.Reader

رابط io.Reader دارای یک متد بنام Read است:

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

این متد تا len(p) بایت داده را از io.Reader به p می‌خواند. تعداد بایت‌های خوانده شده n (0 <= n <= len(p)) و هر خطایی را که برخورده شود برمی‌گرداند.

کد نمونه:

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])
    }
}

در این مثال، ما یک strings.NewReader ایجاد می‌کنیم تا داده را از یک رشته بخوانیم و سپس داده را به اندازه 4 بایت می‌خوانیم.

io.Writer

رابط io.Writer دارای یک متد بنام Write است:

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

این متد داده را از p به جریان داده‌ی زیرین می‌نویسد و تعداد بایت‌های نوشته شده و هر خطایی را که برخورده شود برمی‌گرداند.

کد نمونه:

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)
}

در این مثال، یک رشته ساده را به خروجی استاندارد os.Stdout که به عنوان یک پیاده‌سازی از io.Writer عمل می‌کند، می‌نویسد.

2.3 توابع پیشرفته خواندن/نوشتن

بسته io تعدادی توابع پیشرفته را فراهم می‌کند که می‌تواند وظایف مشترک را ساده‌تر کند، مانند کپی کردن داده و خواندن مقدار مشخصی از داده.

تابع کپی

io.Copy یک روش مفید برای مستقیم کپی کردن داده از یک io.Reader به یک io.Writer بدون نیاز به یک بافر واسط است.

کد نمونه:

package main

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

func main() {
    r := strings.NewReader("مثال عملیات کپی ساده")
    _, err := io.Copy(os.Stdout, r)
    if err != nil {
        panic(err)
    }
}

در این مثال، ما مستقیما یک رشته را به خروجی استاندارد کپی می‌کنیم.

تابع ReadAtLeast

تابع io.ReadAtLeast برای اطمینان از اینکه حداقل یک مقدار مشخصی از یک io.Reader قبل از بازگشت خوانده شود، استفاده می‌شود.

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

کد نمونه:

package main

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

func main() {
    r := strings.NewReader("وب‌سایت Go Language به زبان چینی")
    buf := make([]byte, 14)
    n, err := io.ReadAtLeast(r, buf, 14)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%s\n", buf[:n])
}

در این مثال، io.ReadAtLeast تلاش می‌کند حداقل ۱۴ بایت داده را به buf بخواند.

این توابع پیشرفته خواندن/نوشتن به شما امکان می‌دهد که وظایف مربوط به ورودی/خروجی را به صورت موثر‌تر مدیریت کرده و پایه‌ای قوی برای ساخت منطق برنامه‌های پیچیده‌تر فراهم می‌کند.