C#: عمليات الإدخال/الإخراج للملفات

1. ملف الفئة الثابتة

توفر فئة File طرقًا ثابتة لإجراء العمليات المتعلقة بالملفات، وهي مناسبة لعمليات القراءة/الكتابة التي تتم مرة واحدة أو للعمليات البسيطة. وتشمل الطرق الشائعة: ReadAllText، وWriteAllText، وAppendAllText، وExists، وCopy، وMove، وDelete، وReadAllLines.

▶ مثال

CSHARP
using System;
using System.IO;

string path = "test.txt";

File.WriteAllText(path, "Hello, World!");
string content = File.ReadAllText(path);
Console.WriteLine(content);

File.AppendAllText(path, "\nAppended line.");
string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
{
    Console.WriteLine($"Line: {line}");
}

Console.WriteLine($"Exists: {File.Exists(path)}");

File.Copy(path, "copy.txt", overwrite: true);
File.Move("copy.txt", "moved.txt");
File.Delete("moved.txt");
File.Delete(path);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Hello, World!
Line: Hello, World!
Line: Appended line.
Exists: True

2. فئة الدليل الثابتة

توفر فئة «Directory» طرقًا ثابتة لإجراء العمليات المتعلقة بالدلائل، بما في ذلك إنشاء الدلائل والملفات وحذفها والتحقق من وجودها وعرض قائمة بالدلائل الفرعية والملفات.

▶ مثال

CSHARP
using System;
using System.IO;

string dir = "MyFolder";

Directory.CreateDirectory(dir);
Console.WriteLine($"Exists: {Directory.Exists(dir)}");

File.WriteAllText(Path.Combine(dir, "a.txt"), "aaa");
File.WriteAllText(Path.Combine(dir, "b.txt"), "bbb");

Directory.CreateDirectory(Path.Combine(dir, "SubDir"));

string[] files = Directory.GetFiles(dir);
Console.WriteLine("Files:");
foreach (string f in files)
{
    Console.WriteLine($"  {Path.GetFileName(f)}");
}

string[] dirs = Directory.GetDirectories(dir);
Console.WriteLine("Dirs:");
foreach (string d in dirs)
{
    Console.WriteLine($"  {Path.GetFileName(d)}");
}

Directory.Delete(dir, recursive: true);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Exists: True
Files:
  a.txt
  b.txt
Dirs:
  SubDir

3. فئتا FileInfo و DirectoryInfo

تُعدّ «FileInfo» و«DirectoryInfo» فئتين للمثيلات، وهما مناسبتان لتنفيذ عمليات متعددة على نفس الملف أو الدليل. فهما تتجنبان التكرار في التحقق من صحة المسار وتوفران أداءً أفضل.

▶ مثال

CSHARP
using System;
using System.IO;

FileInfo fi = new FileInfo("demo.txt");
fi.WriteAllText("File info demo");
Console.WriteLine($"Name: {fi.Name}");
Console.WriteLine($"Length: {fi.Length}");
Console.WriteLine($"Extension: {fi.Extension}");
Console.WriteLine($"FullName: {fi.FullName}");
fi.Delete();

DirectoryInfo di = new DirectoryInfo("DemoDir");
di.Create();
Console.WriteLine($"Dir exists: {di.Exists}");
di.CreateSubdirectory("Child");
foreach (DirectoryInfo child in di.GetDirectories())
{
    Console.WriteLine($"Sub: {child.Name}");
}
di.Delete(recursive: true);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Name: demo.txt
Length: 14
Extension: .txt
FullName: ...\demo.txt
Dir exists: True
Sub: Child

4. فئة StreamAbstract و FileStream

«Stream» هي الفئة الأساسية المجردة لجميع التدفقات، وتوفر طرق «Read» و«Write» و«Seek». أما «FileStream» فهي الفئة المشتقة منها التي تُنفذ عمليات القراءة والكتابة على مستوى البايتات.

▶ مثال

CSHARP
using System;
using System.IO;
using System.Text;

string filePath = "data.bin";

using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
    byte[] data = Encoding.UTF8.GetBytes("FileStream demo");
    fs.Write(data, 0, data.Length);
}

using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
    byte[] buffer = new byte[fs.Length];
    fs.Read(buffer, 0, buffer.Length);
    Console.WriteLine(Encoding.UTF8.GetString(buffer));
}

File.Delete(filePath);
▶ جرّب الكود
TEXT 📖 للعرض فقط
FileStream demo

5. StreamReader و StreamWriter

يُعد كل من StreamReader وStreamWriter قارئًا وكاتبًا موجهين للنصوص، حيث يقومان بتغليف معالجة الترميز، مما يجعلهما أسهل في الاستخدام مقارنةً بـ FileStream. يقرأ StreamReader سطراً سطراً عبر الدالة ReadLine، بينما يكتب StreamWriter عبر الدالة WriteLine.

▶ مثال

CSHARP
using System;
using System.IO;

string path = "text.txt";

using (StreamWriter writer = new StreamWriter(path))
{
    writer.WriteLine("First line");
    writer.WriteLine("Second line");
    writer.Write("No newline here ");
    writer.Write("appended.");
}

using (StreamReader reader = new StreamReader(path))
{
    string? line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

File.Delete(path);
▶ جرّب الكود
TEXT 📖 للعرض فقط
First line
Second line
No newline here appended.

6. BinaryReader و BinaryWriter

يُستخدم كل من BinaryReader و BinaryWriter لقراءة وكتابة البيانات الثنائية، حيث يدعمان أنواعًا أولية متنوعة (int، double، string، إلخ). وهما مناسبان للتعامل مع التنسيقات غير النصية.

▶ مثال

CSHARP
using System;
using System.IO;

string path = "binary.dat";

using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
{
    bw.Write(42);
    bw.Write(3.14);
    bw.Write("hello");
}

using (BinaryReader br = new BinaryReader(File.Open(path, FileMode.Open)))
{
    int num = br.ReadInt32();
    double d = br.ReadDouble();
    string s = br.ReadString();
    Console.WriteLine($"Int: {num}, Double: {d}, String: {s}");
}

File.Delete(path);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Int: 42, Double: 3.14, String: hello

7. فئة مساعدة المسار

توفر فئة Path طرقًا ثابتة لإجراء العمليات المتعلقة بالمسارات، حيث تتعامل مع فواصل المسارات بطريقة متوافقة مع جميع الأنظمة الأساسية لتجنب الأخطاء الناتجة عن ربط السلاسل يدويًّا. وتشمل الطرق الشائعة: Combine وGetExtension وGetFileName وGetDirectoryName وGetTempPath.

▶ مثال

CSHARP
using System;
using System.IO;

string fullPath = Path.Combine("folder", "sub", "file.txt");
Console.WriteLine($"Combine: {fullPath}");
Console.WriteLine($"Extension: {Path.GetExtension(fullPath)}");
Console.WriteLine($"FileName: {Path.GetFileName(fullPath)}");
Console.WriteLine($"FileNameNoExt: {Path.GetFileNameWithoutExtension(fullPath)}");
Console.WriteLine($"DirectoryName: {Path.GetDirectoryName(fullPath)}");
Console.WriteLine($"TempPath: {Path.GetTempPath()}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
Combine: folder\sub\file.txt
Extension: .txt
FileName: file.txt
FileNameNoExt: file
DirectoryName: folder\sub
TempPath: ...\Temp\

8. استخدام Statement و IDisposable

يجب تحرير الموارد، مثل الملفات والدفقات، على الفور. تضمن عبارة using استدعاء الدالة Dispose حتى في حالة حدوث استثناء. يدعم C# 8+ صيغة الإعلان using var. جميع فئات الموارد التي تحتاج إلى التنظيف تُنفِّذ واجهة IDisposable.

▶ مثال

CSHARP
using System;
using System.IO;

string path = "using_demo.txt";

using (StreamWriter sw = new StreamWriter(path))
{
    sw.WriteLine("Block using");
}

using StreamReader sr = new StreamReader(path);
Console.WriteLine(sr.ReadLine());

File.Delete(path);

Console.WriteLine(typeof(Stream).GetInterface("IDisposable") != null);
▶ جرّب الكود
TEXT 📖 للعرض فقط
Block using
True

❓ أسئلة شائعة

س هل يجب عليّ استخدام File أم FileInfo؟
ج بالنسبة للعمليات التي تُجرى مرة واحدة، تُعدُّ الطرق الثابتة في File أكثر إيجازًا؛ أما بالنسبة للعمليات المتعددة على الملف نفسه، فإن FileInfo توفر أداءً أفضل.
س ما الفرق بين FileStream و StreamReader؟
ج يقوم FileStream بقراءة وكتابة البايتات؛ بينما يقوم StreamReader بقراءة وكتابة النص مع معالجة الترميز تلقائيًّا. يُفضل استخدام StreamReader في العمليات المتعلقة بالنصوص.
س هل سيتم تحرير الموارد في حالة حدوث استثناء داخل عبارة using؟
ج نعم — يتم ترجمة عبارة using إلى try/finally، والتي تستدعي الدالة Dispose حتى في حالة حدوث استثناء.
س لماذا لا ينبغي ربط المسارات باستخدام سلاسل نصية؟
ج تستخدم أنظمة التشغيل المختلفة فواصل مسارات مختلفة. وتقوم دالة Path.Combine بربط المسارات عبر الأنظمة المختلفة بأمان.

📖 ملخص

📝 تمارين

  1. اكتب برنامجًا يستخدم الدالة File.WriteAllText لحفظ قصيدة، ثم يقرأها ويعرضها باستخدام الدالة File.ReadAllText
  2. استخدم الدالة Directory.CreateDirectory لإنشاء دليل متعدد المستويات، ثم قم بالتكرار وعرض الدلائل الفرعية والملفات الموجودة فيه
  3. استخدم StreamWriter لكتابة 5 أسطر من إدخالات السجل، ثم استخدم StreamReader لقراءتها سطراً سطراً وطباعة أرقام الأسطر
  4. استخدم BinaryWriter لكتابة مجموعة من القيم من نوع int، ثم استخدم BinaryReader لقراءتها مرة أخرى وحساب مجموعها
  5. استخدم الدالة Path.Combine لدمج مسار، ثم اعرض اسم الملف وامتداده واسم المجلد بشكل منفصل
Web-Tutorial.com

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

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

100%