Kodax

File I/O والتعامل مع الملفات — C++ المتقدم

الملفات (File I/O)

الملفات في C++ تدفقات (streams) مثل cin و cout تماماً: نفس << و >> و getline. وهي تطبق RAII: الملف يُغلق تلقائياً عند خروج الكائن من النطاق.

| الصنف | الاستخدام |

|---|---|

| ofstream | كتابة |

| ifstream | قراءة |

| fstream | قراءة وكتابة |

| stringstream | تدفق داخل نص (مفيد للتحليل) |

الكتابة

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ofstream out("notes.txt");            // يُنشئ الملف أو يفرّغه
    if (!out) {                           // افحص دائماً
        cerr << "تعذر فتح الملف\n";
        return 1;
    }
    out << "السطر الأول\n";
    out << "الدرجة: " << 95 << '\n';
}                                         // يُغلق تلقائياً هنا

أوضاع الفتح

| الوضع | المعنى |

|---|---|

| ios::out | كتابة (الافتراضي لـ ofstream) — يفرّغ الملف |

| ios::app | إضافة إلى النهاية دون حذف |

| ios::in | قراءة |

| ios::binary | ثنائي: بلا تحويل لرموز نهاية السطر |

| ios::trunc | تفريغ صريح |

ofstream log("app.log", ios::app); — لملفات السجل.

القراءة الصحيحة

#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    { ofstream("notes.txt") << "السطر الأول\nالسطر الثاني\n"; }   // ملف تجريبي للقراءة
    ifstream in("notes.txt");
    if (!in) { cerr << "الملف غير موجود\n"; return 1; }
    string line;
    int n = 0;
    while (getline(in, line)) {           // ✅ الشرط هو نجاح القراءة نفسها
        cout << ++n << ": " << line << '\n';
    }
}

⚠️ لا تكتب while (!in.eof()): eof لا تصبح true إلا بعد فشل محاولة قراءة، فتعالج السطر الأخير مرتين أو سطراً فارغاً. اجعل القراءة هي الشرط دائماً.

تحليل CSV بـ stringstream

#include <sstream>
#include <string>
#include <vector>
using namespace std;

struct Row { string name; int score; };

bool parseRow(const string& line, Row& out) {
    stringstream ss(line);
    string scoreText;
    if (!getline(ss, out.name, ',') || !getline(ss, scoreText)) return false;
    try { out.score = stoi(scoreText); } catch (...) { return false; }
    return true;
}
// parseRow("سارة,91", r) → true, r.name == "سارة", r.score == 91

getline(ss, field, ',') تقرأ حتى الفاصلة. (CSV الحقيقي قد يحتوي فواصل داخل علامات تنصيص — للمشاريع الجادة استخدم مكتبة.)

الملفات الثنائية

تكتب البايتات كما هي في الذاكرة: أسرع وأصغر، لكنها غير مقروءة للإنسان وغير محمولة بين الأنظمة المختلفة (حجم الأنواع وترتيب البايتات). مناسبة لأنواع بسيطة مثل مصفوفات الأعداد:

#include <fstream>
#include <vector>
using namespace std;

void saveInts(const vector<int>& v) {
    ofstream out("data.bin", ios::binary);
    out.write(reinterpret_cast<const char*>(v.data()), v.size() * sizeof(int));
}

لا تكتب صنفاً فيه string أو مؤشرات بهذه الطريقة — ستكتب عناوين لا المحتوى.

std::filesystem (C++17)

للتعامل مع الملفات نفسها لا محتواها:

#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;

void info(const fs::path& p) {
    if (fs::exists(p)) std::cout << p << " الحجم: " << fs::file_size(p) << " بايت\n";
    // أيضاً: fs::create_directories، fs::remove، fs::directory_iterator
}

تمرين

اكتب برنامج "دفتر درجات": يقرأ grades.csv (اسم,درجة)، يتجاهل الأسطر غير الصالحة مع طباعة رقمها، ويكتب report.txt فيه المتوسط وأعلى طالب. أضف سطراً في log.txt بوضع الإضافة عند كل تشغيل.

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <iomanip>
using namespace std;

struct Student {
    int id;
    string name;
    double gpa;
    
    string toString() const {
        return to_string(id) + "," + name + "," + to_string(gpa);
    }
    
    static Student fromString(const string& line) {
        Student s;
        stringstream ss(line);
        string token;
        
        getline(ss, token, ',');
        s.id = stoi(token);
        
        getline(ss, s.name, ',');
        
        getline(ss, token, ',');
        s.gpa = stod(token);
        
        return s;
    }
};

int main() {
    // ===== كتابة نص =====
    cout << "=== كتابة نص ===" << endl;
    {
        ofstream file("output.txt");
        if (!file.is_open()) {
            cerr << "فشل فتح الملف!" << endl;
            return 1;
        }
        
        file << "مرحباً بالعالم!" << endl;
        file << "سطر ثاني" << endl;
        file << "عدد: " << 42 << endl;
        
        file.close();
        cout << "✅ تم إنشاء output.txt" << endl;
    }
    
    // ===== قراءة نص =====
    cout << "\n=== قراءة نص ===" << endl;
    {
        ifstream file("output.txt");
        if (file.is_open()) {
            string line;
            while (getline(file, line)) {
                cout << "> " << line << endl;
            }
            file.close();
        }
    }
    
    // ===== إضافة للملف =====
    cout << "\n=== إضافة ===" << endl;
    {
        ofstream file("output.txt", ios::app);
        file << "سطر جديد مضاف!" << endl;
        file.close();
        cout << "✅ تم الإضافة" << endl;
    }
    
    // ===== CSV =====
    cout << "\n=== كتابة CSV ===" << endl;
    {
        ofstream file("students.csv");
        file << "ID,Name,GPA" << endl;
        
        vector<Student> students = {
            {1, "أحمد", 3.8},
            {2, "سارة", 3.9},
            {3, "محمد", 3.5},
            {4, "فاطمة", 4.0}
        };
        
        for (const auto& s : students) {
            file << s.toString() << endl;
        }
        
        file.close();
        cout << "✅ تم إنشاء students.csv" << endl;
    }
    
    // ===== قراءة CSV =====
    cout << "\n=== قراءة CSV ===" << endl;
    {
        ifstream file("students.csv");
        string line;
        
        getline(file, line);  // تخطي العنوان
        
        vector<Student> loaded;
        while (getline(file, line)) {
            loaded.push_back(Student::fromString(line));
        }
        
        cout << left << setw(5) << "ID" 
             << setw(15) << "Name" 
             << setw(5) << "GPA" << endl;
        cout << string(25, '-') << endl;
        
        for (const auto& s : loaded) {
            cout << left << setw(5) << s.id 
                 << setw(15) << s.name 
                 << setw(5) << fixed << setprecision(1) << s.gpa << endl;
        }
        
        file.close();
    }
    
    // ===== ملف ثنائي =====
    cout << "\n=== ملف ثنائي ===" << endl;
    {
        // كتابة
        ofstream out("data.bin", ios::binary);
        vector<int> numbers = {10, 20, 30, 40, 50};
        out.write(reinterpret_cast<char*>(numbers.data()), 
                  numbers.size() * sizeof(int));
        out.close();
        cout << "✅ تم كتابة " << numbers.size() << " أرقام" << endl;
        
        // قراءة
        ifstream in("data.bin", ios::binary);
        vector<int> loaded(5);
        in.read(reinterpret_cast<char*>(loaded.data()), 
                loaded.size() * sizeof(int));
        in.close();
        
        cout << "الأرقام المقروءة: ";
        for (int n : loaded) cout << n << " ";
        cout << endl;
    }
    
    // ===== التحقق من وجود ملف =====
    cout << "\n=== التحقق ===" << endl;
    {
        ifstream test("nonexistent.txt");
        if (test.is_open()) {
            cout << "الملف موجود" << endl;
            test.close();
        } else {
            cout << "الملف غير موجود" << endl;
        }
    }
    
    // ===== stringstream =====
    cout << "\n=== stringstream ===" << endl;
    {
        stringstream ss;
        ss << "مرحباً " << 42 << " " << 3.14;
        
        string word;
        int num;
        double pi;
        
        ss >> word >> num >> pi;
        cout << "word: " << word << endl;
        cout << "num: " << num << endl;
        cout << "pi: " << pi << endl;
    }
    
    return 0;
}
اختبر فهمك