لماذا العمل مع الملفات في Go ليس مخيفًا، بل مثيرًا للاهتمام؟
\n\nعندما فتحت لأول مرة وثائق حزمة os في Go، ظننت أنني سأواجه تعدادًا مملًا لاستدعاءات النظام. ولكن بعد التعمق أكثر، أدركت: العمل مع الملفات ونظام الملفات في Go ليس مجرد قراءة للبايتات. إنها أداة قوية تمنحك تحكمًا كاملاً في البيانات، سواء كانت ملفات إعدادات، سجلات، ملفات ثنائية، أو حتى قواعد بيانات كاملة بصيغة CSV.
تم تصميم Go للبرمجة النظامية، لذلك قام مؤلفوه (كين تومسون، روب بايك وآخرون) بدمج العمل مع الملفات مباشرة في جوهر اللغة. لا تبعيات خارجية، فقط مكتبة قياسية تعمل بسرعة ويمكن التنبؤ بها. في هذه المقالة، سنغطي جميع السيناريوهات: من قراءة ملف بسيطة إلى اجتياز الدلائل بشكل متكرر ومعالجة البيانات الكبيرة بشكل متدفق.
\n\nإذا كنت تكتب بلغة Go وتريد العمل بثقة مع نظام الملفات، فأنت في المكان الصحيح. هيا بنا!
\n\nالأساسيات: الفتح، القراءة، الإغلاق
\n\nالخطوة الأولى هي فتح الملف. في Go، تتولى ذلك الدالة os.Open(). وهي تُرجع مؤشرًا للملف ورسالة خطأ. الأخطاء في Go ليست استثناءات، بل قيم. يجب التحقق منها دائمًا.
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("example.txt")\n if err != nil {\n fmt.Println("خطأ في فتح الملف:", err)\n return\n }\n defer file.Close() // يجب إغلاق الملف\n\n // قراءة المحتوى\n buf := make([]byte, 1024)\n n, err := file.Read(buf)\n if err != nil {\n fmt.Println("خطأ في القراءة:", err)\n return\n }\n fmt.Printf("تمت قراءة %d بايت: %s\\n", n, string(buf[:n]))\n}\n\n\nلاحظ استخدام defer file.Close(). هذا أسلوب Go: الاستدعاء المؤجل يضمن إغلاق الملف حتى في حالة حدوث ذعر أو عودة مبكرة. نسيان إغلاق الملف هو ممارسة سيئة تؤدي إلى تسرب مؤشرات الملفات.
قراءة الملف بأكمله بسطر واحد
\n\nإذا كان الملف صغيرًا (حتى بضعة ميغابايت)، يمكنك استخدام os.ReadFile() (الذي حل محل ioutil.ReadFile القديم). هذا مناسب لملفات الإعدادات أو البيانات النصية.
data, err := os.ReadFile("config.yaml")\nif err != nil {\n log.Fatal(err)\n}\nfmt.Println(string(data))\n\n\nالدالة تفتح وتقرأ وتغلق الملف بنفسها. لكن تذكر: بالنسبة للملفات الكبيرة، هذا الأسلوب خطير - سيتم تحميل الملف بأكمله في الذاكرة.
\n\nالكتابة في ملف: الإنشاء والإضافة
\n\nللكتابة، استخدم os.Create() (ينشئ ملفًا جديدًا أو يقطع ملفًا موجودًا) و os.OpenFile() لإعدادات أكثر دقة (مثل الإضافة إلى النهاية).
// إنشاء ملف\nfile, err := os.Create("output.txt")\nif err != nil {\n panic(err)\n}\ndefer file.Close()\n\n// كتابة سلسلة نصية\n_, err = file.WriteString("مرحبًا، ملفات Go!\\n")\nif err != nil {\n panic(err)\n}\n\n// كتابة بايتات\nbytes := []byte("كتابة شريحة بايت\\n")\n_, err = file.Write(bytes)\nif err != nil {\n panic(err)\n}\n\n\nلإضافة بيانات إلى نهاية ملف موجود، استخدم الأعلام:
\n\nfile, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\nif err != nil {\n log.Fatal(err)\n}\ndefer file.Close()\n\nfile.WriteString("إدخال جديد في السجل\\n")\n\n\nالعلم os.O_APPEND يضمن إضافة البيانات إلى النهاية، وليس الكتابة فوق البداية.
العمل مع الدلائل: الإنشاء، الحذف، الاجتياز
\n\nنظام الملفات ليس مجرد ملفات، بل مجلدات أيضًا. توفر Go دوالًا مريحة للعمل مع الدلائل.
\n\nإنشاء وحذف المجلدات
\n\n// إنشاء مجلد واحد\nerr := os.Mkdir("newdir", 0755)\nif err != nil {\n log.Fatal(err)\n}\n\n// إنشاء مجلدات متداخلة (مثل mkdir -p)\nerr = os.MkdirAll("path/to/nested/dir", 0755)\nif err != nil {\n log.Fatal(err)\