C#: عمليات الإدخال/الإخراج للملفات
1. ملف الفئة الثابتة
توفر فئة File طرقًا ثابتة لإجراء العمليات المتعلقة بالملفات، وهي مناسبة لعمليات القراءة/الكتابة التي تتم مرة واحدة أو للعمليات البسيطة. وتشمل الطرق الشائعة: ReadAllText، وWriteAllText، وAppendAllText، وExists، وCopy، وMove، وDelete، وReadAllLines.
▶ مثال
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);
Hello, World!
Line: Hello, World!
Line: Appended line.
Exists: True
2. فئة الدليل الثابتة
توفر فئة «Directory» طرقًا ثابتة لإجراء العمليات المتعلقة بالدلائل، بما في ذلك إنشاء الدلائل والملفات وحذفها والتحقق من وجودها وعرض قائمة بالدلائل الفرعية والملفات.
▶ مثال
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);
Exists: True
Files:
a.txt
b.txt
Dirs:
SubDir
3. فئتا FileInfo و DirectoryInfo
تُعدّ «FileInfo» و«DirectoryInfo» فئتين للمثيلات، وهما مناسبتان لتنفيذ عمليات متعددة على نفس الملف أو الدليل. فهما تتجنبان التكرار في التحقق من صحة المسار وتوفران أداءً أفضل.
▶ مثال
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);
Name: demo.txt
Length: 14
Extension: .txt
FullName: ...\demo.txt
Dir exists: True
Sub: Child
4. فئة StreamAbstract و FileStream
«Stream» هي الفئة الأساسية المجردة لجميع التدفقات، وتوفر طرق «Read» و«Write» و«Seek». أما «FileStream» فهي الفئة المشتقة منها التي تُنفذ عمليات القراءة والكتابة على مستوى البايتات.
▶ مثال
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);
FileStream demo
5. StreamReader و StreamWriter
يُعد كل من StreamReader وStreamWriter قارئًا وكاتبًا موجهين للنصوص، حيث يقومان بتغليف معالجة الترميز، مما يجعلهما أسهل في الاستخدام مقارنةً بـ FileStream. يقرأ StreamReader سطراً سطراً عبر الدالة ReadLine، بينما يكتب StreamWriter عبر الدالة WriteLine.
▶ مثال
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);
First line
Second line
No newline here appended.
6. BinaryReader و BinaryWriter
يُستخدم كل من BinaryReader و BinaryWriter لقراءة وكتابة البيانات الثنائية، حيث يدعمان أنواعًا أولية متنوعة (int، double، string، إلخ). وهما مناسبان للتعامل مع التنسيقات غير النصية.
▶ مثال
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);
Int: 42, Double: 3.14, String: hello
7. فئة مساعدة المسار
توفر فئة Path طرقًا ثابتة لإجراء العمليات المتعلقة بالمسارات، حيث تتعامل مع فواصل المسارات بطريقة متوافقة مع جميع الأنظمة الأساسية لتجنب الأخطاء الناتجة عن ربط السلاسل يدويًّا. وتشمل الطرق الشائعة: Combine وGetExtension وGetFileName وGetDirectoryName وGetTempPath.
▶ مثال
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()}");
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.
▶ مثال
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);
Block using
True
❓ أسئلة شائعة
File أم FileInfo؟File أكثر إيجازًا؛ أما بالنسبة للعمليات المتعددة على الملف نفسه، فإن FileInfo توفر أداءً أفضل.📖 ملخص
- تُعد فئة الملفات الثابتة مناسبة لعمليات الملفات التي تُجرى مرة واحدة
- تتولى الفئة الثابتة «Directory» إدارة عمليات إنشاء الدلائل وحذفها والتصفح فيها
- تُعد فئات مثيلات FileInfo/DirectoryInfo أكثر ملاءمةً لإجراء عمليات متعددة على نفس الهدف
- Stream هي الفئة الأساسية المجردة لـ «التيارات»؛ بينما توفر FileStream القراءة والكتابة على مستوى البايت
- تعمل StreamReader وStreamWriter على تغليف عملية الترميز وتبسيط عمليات الإدخال/الإخراج النصية
- BinaryReader/BinaryWriter يعالجان البيانات ذات الأنواع الأولية الثنائية
- تتولى فئة «Path» معالجة عمليات المسارات عبر الأنظمة الأساسية بأمان
- يضمن استخدام عبارة "using" تحرير الموارد التي تتبع واجهة IDisposable على الفور
📝 تمارين
- اكتب برنامجًا يستخدم الدالة File.WriteAllText لحفظ قصيدة، ثم يقرأها ويعرضها باستخدام الدالة File.ReadAllText
- استخدم الدالة Directory.CreateDirectory لإنشاء دليل متعدد المستويات، ثم قم بالتكرار وعرض الدلائل الفرعية والملفات الموجودة فيه
- استخدم StreamWriter لكتابة 5 أسطر من إدخالات السجل، ثم استخدم StreamReader لقراءتها سطراً سطراً وطباعة أرقام الأسطر
- استخدم BinaryWriter لكتابة مجموعة من القيم من نوع int، ثم استخدم BinaryReader لقراءتها مرة أخرى وحساب مجموعها
- استخدم الدالة Path.Combine لدمج مسار، ثم اعرض اسم الملف وامتداده واسم المجلد بشكل منفصل