Go: معالجة ملفات الإدخال/الإخراج (I/O) وملفات JSON

آخر تحديث: 2026-08-26

يُعد كل من إدخال/إخراج الملفات وJSON مهارتين أساسيتين في تطوير الباك إند بلغة Go — حيث توفر حزمة os عمليات الملفات على مستوى استدعاءات النظام، بينما تتيح حزمة encoding/json التواصل السلس بين لغة Go وعالم JavaScript.

صُممت عمليات الملفات ومعالجة JSON في المكتبة القياسية لـ Go باتباع نهج ذكي وموحد: فهي تستند جميعها إلى واجهتي io.Reader وio.Writer. في هذا الدرس، ستتقن جميع المهارات الأساسية المتعلقة بعمليات الإدخال/الإخراج للملفات ومعالجة JSON.

1. ستتعلم



2. القصة الحقيقية لمهندس متكامل

(1) المشكلة: ربط سلاسل JSON يدويًّا

تشارلي هو مهندس برمجيات متكامل يحتاج إلى تصدير بيانات المستخدمين من قاعدة البيانات إلى ملف JSON لاستخدامها في الواجهة الأمامية:

«لم أرغب في استخدام مكتبة خارجية، لذا قمت بإنشاء سلسلة JSON يدويًّا. وتبين لاحقًا أن حقل اسم المستخدم احتوى على الحرف \"، مما أدى إلى إفساد تنسيق JSON تمامًا. ولم يظهر الخطأ إلا بعد أن كنت قد وصلت إلى منتصف عملية تصدير مليون صف من البيانات — واستغرق الأمر ساعتين إضافيتين لإلغاء التغييرات.»

فتح الكود الذي كان قد كتبه:

GO
// Bad code: manually concatenating JSON strings
func exportUserJSON(users []User) string {
    result := "["
    for i, u := range users {
        if i > 0 {
            result += ","
        }
        // Manual concatenation, double quotes not escaped
        result += "{\"name\":\"" + u.Name + "\",\"age\":" + string(u.Age) + "}"
    }
    result += "]"
    return result
}

إذا احتوى u.Name على " أو \، فسيصبح ملف JSON الذي تم إنشاؤه تالفًا. علاوة على ذلك، يقوم string(u.Age) بتحويل الأرقام إلى رموز ASCII — فيصبح 28 هو \x1c.

(2) حل Go: encoding/json + عمليات الإدخال/الإخراج للملفات

GO
// json_exporter.go
package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    City string `json:"city"`
}

func main() {
    users := []User{
        {Name: "Alice", Age: 28, City: "Shanghai"},
        {Name: `Bob "The Builder"`, Age: 32, City: "Beijing"},
        {Name: "Charlie", Age: 25, City: "Guangzhou"},
    }

    // JSON serialization to file (no manual concatenation needed)
    file, _ := os.Create("users.json")
    defer file.Close()

    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    encoder.Encode(users)

    fmt.Println("Export successful!")

    // Verification: read it back
    data, _ := os.ReadFile("users.json")
    fmt.Println(string(data))
}

الناتج:

TEXT 📖 للعرض فقط
Export successful!
[
  {
    "name": "Alice",
    "age": 28,
    "city": "Shanghai"
  },
  {
    "name": "Bob \"The Builder\"",
    "age": 32,
    "city": "Beijing"
  },
  {
    "name": "Charlie",
    "age": 25,
    "city": "Guangzhou"
  }
]

(3) المزايا: مقارنة بين طرق معالجة JSON

الطريقة الهروب من علامات الاقتباس المزدوجة الأحرف الخاصة الملفات الكبيرة حجم الكود
التسلسل اليدوي ❌ الترميز اليدوي ❌ عرضة للأخطاء ❌ نفاد الذاكرة (OOM) حوالي 50 سطراً
json.Marshal ✅ تلقائي ✅ تلقائي ❌ الذاكرة ممتلئة ~5 أسطر
json.Encoder ✅ تلقائي ✅ تلقائي ✅ قائم على التدفق ~5 أسطر
💡 نصيحة: لا تقم أبدًا بإنشاء JSON يدويًّا — استخدم حزمة encoding/json. فهي تتولى تلقائيًّا عمليات الترميز الهروبي والترميز والتبويب، كما تتعامل بشكل صحيح مع أحرف UTF-8 في لغة Go.



3. عمليات الملفات في نظام التشغيل

(1) فتح الملفات وإنشاؤها

GO
package main

import (
    "fmt"
    "os"
)

func main() {
    // Create (or truncate) file
    f, _ := os.Create("test.txt")
    defer f.Close()

    // Write string
    f.WriteString("Hello, Go Files!\n")
    f.Write([]byte("Second line\n"))

    // Open in append mode
    f2, _ := os.OpenFile("test.txt", os.O_APPEND|os.O_WRONLY, 0644)
    defer f2.Close()
    f2.WriteString("Appended line\n")

    // Read file
    data, _ := os.ReadFile("test.txt")
    fmt.Print(string(data))
}

الناتج:

TEXT 📖 للعرض فقط
Hello, Go Files!
Second line
Appended line

(2) os.ReadFile / os.WriteFile (قراءة/كتابة لمرة واحدة)

GO
package main

import (
    "fmt"
    "os"
)

func main() {
    // Write (one-time)
    content := []byte("line1\nline2\nline3\n")
    err := os.WriteFile("data.txt", content, 0644)
    if err != nil {
        fmt.Printf("Write error: %v\n", err)
        return
    }

    // Read (one-time)
    data, err := os.ReadFile("data.txt")
    if err != nil {
        fmt.Printf("Read error: %v\n", err)
        return
    }
    fmt.Printf("Read %d bytes:\n%s", len(data), data)
}

▶ مثال: نسخ ملف

GO
package main

import (
    "fmt"
    "io"
    "os"
)

func copyFile(src, dst string) (int64, error) {
    sourceFile, err := os.Open(src)
    if err != nil {
        return 0, err
    }
    defer sourceFile.Close()

    destFile, err := os.Create(dst)
    if err != nil {
        return 0, err
    }
    defer destFile.Close()

    // io.Copy uses a default 32KB buffer
    return io.Copy(destFile, sourceFile)
}

func main() {
    // First write the source file
    os.WriteFile("source.txt", []byte("Hello World!\n"), 0644)

    n, err := copyFile("source.txt", "dest.txt")
    if err != nil {
        fmt.Printf("Copy error: %v\n", err)
        return
    }
    fmt.Printf("Copied %d bytes\n", n)

    data, _ := os.ReadFile("dest.txt")
    fmt.Printf("Content: %s", data)
}
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
Copied 13 bytes
Content: Hello World!
🔥 خطأ شائع: يقوم os.ReadFile بقراءة الملف بأكمله في الذاكرة — وهو ما يناسب الملفات الصغيرة (< 100 ميغابايت). أما الملفات الكبيرة (مثل سجلات 1 جيجابايت أو أكثر) فيجب معالجتها باستخدام bufio أو io.Copy من أجل المعالجة التدفقية.



4. bufio: الإدخال/الإخراج المخزن مؤقتًا

(1) يقوم bufio بالقراءة سطراً سطراً

GO
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    // First write a test file
    content := ""
    for i := 1; i <= 100; i++ {
        content += fmt.Sprintf("Line %d\n", i)
    }
    os.WriteFile("large.txt", []byte(content), 0644)

    // Read line by line
    file, _ := os.Open("large.txt")
    defer file.Close()

    scanner := bufio.NewScanner(file)
    lineCount := 0
    for scanner.Scan() {
        lineCount++
        if lineCount <= 3 {
            fmt.Println(scanner.Text())
        }
    }
    fmt.Printf("...total %d lines\n", lineCount)
}

الناتج:

TEXT 📖 للعرض فقط
Line 1
Line 2
Line 3
...total 100 lines

▶ مثال: الكتابة إلى المخزن المؤقت bufio

GO
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, _ := os.Create("buffered.txt")
    defer file.Close()

    writer := bufio.NewWriter(file)
    writer.WriteString("First line\n")
    writer.WriteString("Second line\n")
    writer.WriteString("Third line\n")

    fmt.Printf("Buffer size: %d\n", writer.Buffered())

    // Important: flush the buffer to disk
    writer.Flush()

    data, _ := os.ReadFile("buffered.txt")
    fmt.Printf("Content:\n%s", data)
}
▶ جرّب الكود

(3) os مقابل bufio

الميزة القراءة/الكتابة المباشرة على نظام التشغيل التخزين المؤقت Bufio
الآلية الداخلية استدعاء النظام (استدعاء واحد لكل عملية قراءة/كتابة) مخزن مؤقت في وضع المستخدم (يقلل من عدد استدعاءات النظام)
السيناريوهات المناسبة الملفات الصغيرة، الوصول العشوائي الملفات الكبيرة، القراءة/الكتابة التسلسلية
قراءة سطرًا سطرًا غير مدعوم bufio.Scanner
الأداء (الملفات الكبيرة) بطيء (عدد كبير من استدعاءات النظام) سريع (انخفاض بنسبة 90% في استدعاءات النظام)
المخزن المؤقت الافتراضي غير متوفر 4 كيلوبايت (قابل للتعديل)


5. التسلسل باستخدام encoding/json

(1) json.Marshal / json.Unmarshal

GO
package main

import (
    "encoding/json"
    "fmt"
)

type Product struct {
    Name     string   `json:"name"`
    Price    float64  `json:"price"`
    InStock  bool     `json:"in_stock"`
    Tags     []string `json:"tags,omitempty"`
    Internal string   `json:"-"`  // not serialized
}

func main() {
    p := Product{
        Name:     "Go Mug",
        Price:    19.99,
        InStock:  true,
        Tags:     []string{"gift", "office"},
        Internal: "secret123",
    }

    // Serialize
    data, _ := json.Marshal(p)
    fmt.Printf("Marshaled: %s\n", data)

    // With indentation
    pretty, _ := json.MarshalIndent(p, "", "  ")
    fmt.Printf("Pretty:\n%s\n", pretty)

    // Deserialize
    var p2 Product
    json.Unmarshal([]byte(`{"name":"Go T-Shirt","price":29.99,"in_stock":false}`), &p2)
    fmt.Printf("Unmarshaled: %+v\n", p2)
}

الناتج:

TEXT 📖 للعرض فقط
Marshaled: {"name":"Go Mug","price":19.99,"in_stock":true,"tags":["gift","office"]}
Pretty:
{
  "name": "Go Mug",
  "price": 19.99,
  "in_stock": true,
  "tags": [
    "gift",
    "office"
  ]
}
Unmarshaled: {Name:Go T-Shirt Price:29.99 InStock:false Tags:[] Internal:}

▶ مثال: تحليل JSON ديناميكي (interface{})

GO
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    data := `{"name":"Alice","age":28,"address":{"city":"Shanghai","zip":"200000"}}`

    // Parse into map (no need to pre-define a struct)
    var result map[string]interface{}
    json.Unmarshal([]byte(data), &result)

    fmt.Printf("name: %v\n", result["name"])
    fmt.Printf("age: %v (type=%T)\n", result["age"], result["age"])

    // Nested map requires type assertion (comma-ok safe pattern)
    if addr, ok := result["address"].(map[string]interface{}); ok {
        fmt.Printf("city: %v\n", addr["city"])
    }
}
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
name: Alice
age: 28 (type=float64)
city: Shanghai
💡 نصيحة: يتم تحليل الأرقام في JSON على أنها float64 بشكل افتراضي — لذا يُعرض age على أنه 28، لكن نوعه هو float64. إذا كنت ترغب في استخدام int، فإما أن تحدد النوع باستخدام بنية (struct)، أو تستخدم json.Decoder مع UseNumber().



6. معالجة تدفق البيانات باستخدام أداة التشفير/فك التشفير لـ JSON

▶ مثال: يقوم جهاز التشفير بالكتابة إلى ملف في الوقت الفعلي

GO
package main

import (
    "encoding/json"
    "fmt"
    "os"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    users := []User{
        {Name: "Alice", Age: 28},
        {Name: "Bob", Age: 32},
        {Name: "Charlie", Age: 25},
    }

    // Stream write to file (no complete []byte constructed)
    file, _ := os.Create("users.json")
    defer file.Close()

    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")

    for _, u := range users {
        encoder.Encode(u)  // write one by one
    }

    data, _ := os.ReadFile("users.json")
    fmt.Println(string(data))
}
▶ جرّب الكود

(2) وحدة فك الترميز: القراءة القائمة على الدفق (JSON سطراً سطراً)

GO
package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    // Simulate JSON Lines format (one JSON object per line)
    input := `{"name":"Alice","age":28}
{"name":"Bob","age":32}
{"name":"Charlie","age":25}`

    // Stream decode
    decoder := json.NewDecoder(strings.NewReader(input))
    for {
        var u User
        err := decoder.Decode(&u)
        if err != nil {
            break  // EOF
        }
        fmt.Printf("Decoded: %s (%d)\n", u.Name, u.Age)
    }
}

الناتج:

TEXT 📖 للعرض فقط
Decoded: Alice (28)
Decoded: Bob (32)
Decoded: Charlie (25)

(3) مارشال ضد إنكودر

الخاصية json.Marshal json.Encoder
وجهة الإخراج []byte (الذاكرة) io.Writer (أي وجهة)
استخدام الذاكرة حجم البنية [] بايت مخزن مؤقت الكتابة للدفق
الملفات الكبيرة ❌ خطر نفاد الذاكرة ✅ الأمان
التحكم في المسافات البادئة MarshalIndent SetIndent()
السيناريوهات النموذجية استجابات واجهة برمجة التطبيقات (API) / البيانات الصغيرة كتابة الملفات / تدفقات الشبكة


7. الدليل الشامل لعلامات JSON

(1) خيار العلامة (1)

GO
type Config struct {
    Name     string   `json:"name"`               // field name mapping
    Omit     string   `json:"omit,omitempty"`      // omit on zero value
    Skip     string   `json:"-"`                   // skip this field
    String   int      `json:"string"`              // number converted to string
}

(2) جميع الخيارات

الخيار الصيغة التأثير
إعادة تسمية json:"new_name" تغيير اسم حقل JSON إلى "new_name"
omitempty json:"name,omitempty" تجاهل هذا الحقل إذا كانت القيمة صفرًا
تخطي json:"-" عدم التسلسل/إلغاء التسلسل
سلسلة json:"id,string" تحويل الأرقام إلى سلاسل (متوافق مع JavaScript)
التداخل json:"-" بنية متوسطة التحكم في التسلسل المتداخل

▶ مثال: استخدام omitempty + سلسلة في الممارسة العملية

GO
package main

import (
    "encoding/json"
    "fmt"
)

type APIResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message,omitempty"`  // omitted when empty
    Data    any    `json:"data,omitempty"`      // omitted when nil
    ID      int64  `json:"id,string"`           // int64 → string
}

func main() {
    resp := APIResponse{
        Code: 200,
        Data: nil,     // will be omitted
        ID:   9876543210123,
    }

    data, _ := json.MarshalIndent(resp, "", "  ")
    fmt.Println(string(data))
}
▶ جرّب الكود

الناتج:

TEXT 📖 للعرض فقط
{
  "code": 200,
  "id": "9876543210123"
}


8. مثال كامل: أداة تصدير قاعدة البيانات بتنسيق JSON

GO
// db_exporter.go
package main

import (
    "encoding/json"
    "fmt"
    "os"
    "time"
)

// ---------- Data Model ----------

type Product struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Price     float64   `json:"price"`
    Stock     int       `json:"stock"`
    Category  string    `json:"category,omitempty"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at,omitempty"`
}

type ExportConfig struct {
    FilePath     string `json:"file_path"`
    PrettyPrint  bool   `json:"pretty_print"`
    IncludeMeta  bool   `json:"include_meta"`
}

// ---------- Simulated Database ----------

type Database struct {
    products []Product
}

func NewDatabase() *Database {
    return &Database{
        products: []Product{
            {ID: 1, Name: "Go Mug", Price: 19.99, Stock: 100, Category: "accessories",
                CreatedAt: time.Now()},
            {ID: 2, Name: `Go "Gopher" T-Shirt`, Price: 29.99, Stock: 50, Category: "clothing",
                CreatedAt: time.Now()},
            {ID: 3, Name: "Go Programming Book", Price: 49.99, Stock: 200, Category: "books",
                CreatedAt: time.Now()},
        },
    }
}

func (db *Database) QueryAll() []Product {
    return db.products
}

// ---------- Exporter ----------

type Exporter struct {
    config ExportConfig
}

func NewExporter(config ExportConfig) *Exporter {
    return &Exporter{config: config}
}

func (e *Exporter) ExportToFile(products []Product) error {
    file, err := os.Create(e.config.FilePath)
    if err != nil {
        return fmt.Errorf("create file: %w", err)
    }
    defer file.Close()

    encoder := json.NewEncoder(file)
    if e.config.PrettyPrint {
        encoder.SetIndent("", "  ")
    }

    if e.config.IncludeMeta {
        wrapper := map[string]interface{}{
            "exported_at": time.Now().Format(time.RFC3339),
            "total":       len(products),
            "products":    products,
        }
        return encoder.Encode(wrapper)
    }

    return encoder.Encode(products)
}

func (e *Exporter) ExportToMemory(products []Product) ([]byte, error) {
    if e.config.PrettyPrint {
        return json.MarshalIndent(products, "", "  ")
    }
    return json.Marshal(products)
}

// ---------- Importer (reads JSON file) ----------

type Importer struct{}

func NewImporter() *Importer {
    return &Importer{}
}

func (imp *Importer) ImportFromFile(path string) ([]Product, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("open file: %w", err)
    }
    defer file.Close()

    var products []Product
    decoder := json.NewDecoder(file)
    if err := decoder.Decode(&products); err != nil {
        // Try the wrapped format: must rebuild decoder after Seek
        file.Seek(0, 0)
        decoder = json.NewDecoder(file)
        var wrapped struct {
            Products []Product `json:"products"`
        }
        if err2 := decoder.Decode(&wrapped); err2 != nil {
            return nil, fmt.Errorf("decode: %w", err)
        }
        products = wrapped.Products
    }

    return products, nil
}

func main() {
    db := NewDatabase()
    products := db.QueryAll()

    // 1. Stream JSON write to file
    exporter := NewExporter(ExportConfig{
        FilePath:    "export.json",
        PrettyPrint: true,
        IncludeMeta: true,
    })
    if err := exporter.ExportToFile(products); err != nil {
        fmt.Printf("Export error: %v\n", err)
        return
    }
    fmt.Println("Exported to export.json")

    // 2. Read back to verify
    importer := NewImporter()
    imported, err := importer.ImportFromFile("export.json")
    if err != nil {
        fmt.Printf("Import error: %v\n", err)
        return
    }
    fmt.Printf("Imported %d products\n", len(imported))
    for _, p := range imported {
        fmt.Printf("  %d. %s ($%.2f)\n", p.ID, p.Name, p.Price)
    }

    // 3. Display file content
    data, _ := os.ReadFile("export.json")
    fmt.Printf("\nFile content:\n%s\n", data)
}

النتيجة المتوقعة:

TEXT 📖 للعرض فقط
Exported to export.json
Imported 3 products
  1. Go Mug ($19.99)
  2. Go "Gopher" T-Shirt ($29.99)
  3. Go Programming Book ($49.99)

File content:
{
  "exported_at": "2026-07-08T10:00:00Z",
  "total": 3,
  "products": [
    {
      "id": 1,
      "name": "Go Mug",
      "price": 19.99,
      "stock": 100,
      "category": "accessories",
      "created_at": "2026-07-08T10:00:00Z"
    },
    ...
  ]
}
100%
flowchart LR
    A[Go struct] --> B[json.Marshal]
    A --> C[json.Encoder]
    B --> D["[]byte (memory)"]
    D --> E[os.WriteFile / HTTP Response]
    C --> F[File / net.Conn / bytes.Buffer]
    F --> G[Stream write]
    H[JSON file/stream] --> I[json.Decoder]
    I --> J[Go struct]
    H --> K[json.Unmarshal]
    K --> J
    style A fill:#e1f5fe
    style J fill:#e1f5fe
🔥 خطأ شائع: لا يعمل الخيار omitempty أثناء التصدير بتنسيق JSON مع الأوقات ذات القيمة الصفرية (time.Time{})—فـ time.Time{} ليس nil ولن يتم حذفه. إذا كنت بحاجة إلى حذف الأوقات ذات القيمة الصفرية، فاستخدم نوع المؤشر *time.Time.



❓ أسئلة شائعة

س ما الفرق بين os.ReadFile وioutil.ReadFile؟
ج os.ReadFile هو بديل تم تقديمه في الإصدار Go 1.16؛ وهو يعمل بنفس الطريقة. أصبح ioutil.ReadFile مهملًا بدءًا من الإصدار Go 1.19. يجب عليك استخدام os.ReadFile / os.WriteFile.
س كيف يكون bufio أسرع من عمليات القراءة والكتابة المباشرة باستخدام نظام التشغيل؟
ج يحتفظ bufio بمخزن مؤقت (4 كيلوبايت افتراضيًا) في مساحة المستخدم، مما يقلل بشكل كبير من عدد استدعاءات النظام. أما الاستدعاء المباشر os.File.Read فيؤدي دائمًا إلى استدعاء النظام (مع عبء إضافي يبلغ حوالي 1 µs)، في حين أن bufio يقرأ 4 كيلوبايت في المرة الواحدة، مما يقلل من استدعاءات النظام بنسبة 99.9%.
س كيف أختار بين json.Marshal وjson.Encoder؟
ج بالنسبة لأحجام البيانات التي تقل عن 100 كيلوبايت أو عندما تكون المعالجة داخل الذاكرة مطلوبة → Marshal؛ أما بالنسبة للكتابة إلى الملفات/تدفقات الشبكة أو الملفات الكبيرة → Encoder. ميزة Encoder هي أنها تعمل بطريقة التدفق ولا تستهلك الكثير من الذاكرة.
س كيف يتحكم struct tag في حقول JSON؟
ج json:"name" يعيد تسمية الحقل؛ json:"name,omitempty" يحذف القيم الصفرية؛ json:"-" يتخطى الحقل؛ json:"id,string" يحول الأرقام إلى سلاسل نصية. افصل الخيارات المتعددة بفواصل.
س كيف يتم التعامل مع الترميز غير ASCII في JSON؟
ج تقوم وظيفة encoding/json في لغة Go بإخراج UTF-8 بشكل افتراضي — حيث يتم إخراج الأحرف غير ASCII كما هي. إذا كنت بحاجة إلى إخراج يقتصر على أحرف ASCII فقط (كما تتطلب بعض الأنظمة القديمة)، فاستخدم json.MarshalIndent أو SetEscapeHTML(false).
س كيف يمكنني التعامل مع ملفات JSON الكبيرة؟
ج استخدم json.Decoder لفك الترميز بالتدفق (decoder.Decode(&v) لفك ترميز كائن واحد في كل مرة)، أو تنسيق JSON Lines (كائن JSON واحد في كل سطر). لا تستخدم json.Unmarshal لقراءة الملف بأكمله.
س كيف يمكنني تحليل JSON ذي بنية غير معروفة؟
ج استخدم map[string]interface{} أو json.RawMessage (التحليل المؤجل). map[string]interface{} مرن ولكنه يتطلب تأكيدات الأنواع؛ أما json.RawMessage فيحتفظ بـ JSON الخام لتحليله لاحقًا.
س ما الفرق بين os.Create وos.OpenFile؟
ج os.Create(f) تعادل os.OpenFile(f, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) — فهي تقوم باقتطاع (الكتابة فوق) الملف إذا كان موجودًا بالفعل. os.OpenFile يدعم المزيد من الأوضاع (الإضافة، القراءة فقط، إنشاء ملف جديد، إلخ).

📖 ملخص


📝 تمارين

  1. المسألة الأساسية (الصعوبة ⭐): اكتب برنامجًا يستخدم os.ReadFile لقراءة ملف نصي، ويحسب عدد الأسطر والكلمات والأحرف، ويكتب النتائج في ملف آخر. يجب عليك استخدام strings.Fields وbufio.Scanner وos.WriteFile.

  2. مشكلة متقدمة (درجة الصعوبة ⭐⭐): قم بتنفيذ مدير TodoList يدعم إضافة العناصر وعرضها وإكمالها وحذفها، ويستخدم encoding/json لتسلسل البيانات إلى ملف من أجل الاحتفاظ بها. المتطلبات: استخدم علامات JSON للتحكم في أسماء الحقول، واستخدم omitempty للتعامل مع الحقول الاختيارية.

  3. مشكلة التحدي (الصعوبة ⭐⭐⭐): قم بتنفيذ أداة تصدير متعددة التنسيقات تقوم بتصدير البيانات من مصدر البيانات []Product في آن واحد بثلاثة تنسيقات: JSON، وJSON Lines (كائن JSON واحد في كل سطر)، وCSV. يجب عليك استخدام os.Create + json.Encoder (لـ JSON) / fmt.Fprintf (لـ CSV) وواجهة io.Writer مشتركة.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%