Rust والملفات: خرافات ومخاوف وأساليب فعلية
هل تساءلت يومًا: "كيف يمكن قراءة وكتابة الملفات بشكل صحيح في Rust الحديثة؟". إذا كان الأمر كذلك، فأنت لست وحدك. على Stack Overflow، جمع سؤال "ما هي الطريقة الفعلية لقراءة وكتابة الملفات في Rust 1.x؟" آلاف المشاهدات وعشرات الإجابات. إنها معاناة حقيقية للمجتمع، خاصة لأولئك القادمين من Python أو JavaScript، حيث open() و read() هما الأساس، بينما في Rust عليك التعامل مع الملكية والاقتراض ومجموعة من السمات.
في هذه المقالة، سنقوم بتفصيل جميع الطرق الأساسية للعمل مع الملفات في Rust 1.x: من "مجرد قراءة كل شيء في سلسلة نصية" إلى القراءة الفعالة سطرًا بسطر لسجلات كبيرة. ستتعرف على الأسلوب الذي يعتبر اصطلاحيًا وأيها من الأفضل الاحتفاظ به للحالات الخاصة. هيا بنا!
1. std::fs: الترسانة القياسية
وحدة std::fs هي محطتك الأولى. توفر دوال لقراءة وكتابة ونسخ وحذف الملفات. قامت Rust 1.x بتثبيت أغلفة مريحة تخفي معظم العمل منخفض المستوى مع استدعاءات النظام.
قراءة الملف بأكمله في سلسلة نصية
أبسط طريقة هي استخدام std::fs::read_to_string. يفتح الملف، ويقرأه بالكامل في الذاكرة، ويعيد Result<String>. مثالي لملفات التكوين الصغيرة أو الملفات النصية.
use std::fs;
fn main() -> std::io::Result<()> { let content = fs::read_to_string("hello.txt")?; println!("الملف يحتوي على:{}", content); Ok(())}هذه الطريقة هي المعيار الفعلي للحالات البسيطة. إنها موجزة وآمنة وتستخدم ? لمعالجة الأخطاء. العيب الوحيد هو أن الملف بأكمله يتم تحميله في الذاكرة، لذا فهي غير مناسبة لسجلات بحجم جيجابايت.
القراءة في متجه من البايتات
إذا كنت تعمل مع بيانات ثنائية (صور، أرشيفات)، استخدم std::fs::read. يعيد Vec<u8>.
use std::fs;
fn main() -> std::io::Result<()> { let bytes = fs::read("image.png")?; println!("تمت قراءة {} بايت", bytes.len()); Ok(())}الكتابة في ملف
للكتابة، استخدم std::fs::write. يأخذ مسارًا وبيانات (سلسلة نصية أو مصفوفة بايتات) وينشئ/يكتب فوق الملف.
use std::fs;
fn main() -> std::io::Result<()> { fs::write("output.txt", "Hello, Rust!")?; Ok(())}بسيط جدًا. ولكن إذا كنت بحاجة إلى الإلحاق بنهاية الملف بدلاً من الكتابة فوقه، فسيتعين عليك استخدام OpenOptions.
2. BufReader و BufWriter: عندما يكون الحجم مهمًا
بالنسبة للملفات الكبيرة أو عمليات الإدخال/الإخراج المتكررة، تأتي الأغلفة المخزنة مؤقتًا للإنقاذ. تقلل من عدد استدعاءات النظام، وهو أمر بالغ الأهمية للأداء.
القراءة سطرًا بسطر مع BufReader
مثال كلاسيكي هو قراءة ملف سجل كبير سطرًا بسطر:
use std::fs::File;use std::io::{BufRead, BufReader};
fn main() -> std::io::Result<()> { let file = File::open("large_log.txt")?; let reader = BufReader::new(file);
for line in reader.lines() { let line = line?; println!("{}", line); } Ok(())}طريقة lines() تعيد مكررًا للأسطر، كل منها هو Result<String>. لاحظ line? داخل الحلقة - هذا هو معالجة أخطاء قراءة كل سطر.
هام: lines() تزيل أحرف السطر الجديد. إذا كنت بحاجة إلى الاحتفاظ بها، استخدم read_until أو اقرأ الملف كـ Vec<u8>.
الكتابة مع BufWriter
عند كتابة كميات كبيرة من البيانات، يكون التخزين المؤقت مفيدًا أيضًا:
use std::fs::File;use std::io::{BufWriter, Write};
fn main() -> std::io::Result<