كتابة الملفات
كتابة الملفات
قراءة الملف ليست سوى نصف القصة. معرفة كيفية الكتابة — واختيار الأداة المناسبة — لا تقل أهمية. تمنحك Java عدة خيارات: الدوال الحديثة Files.write وFiles.writeString من NIO.2، والكلاسيكي BufferedWriter المبني على واجهة I/O القديمة. لكل منها مجاله المثالي، وكل منها يتيح لك الاختيار بين الكتابة فوق الملف الحالي والإلحاق بنهايته.
الطريق السريع: Files.write و Files.writeString
منذ Java 7 مع NIO.2، توفّر java.nio.file.Files دوالًا ساكنة تفتح الملف وتكتب فيه وتغلقه بنداء واحد، دون الحاجة إلى إدارة التدفقات يدويًا.
كتابة قائمة من الأسطر:
كل عنصر في القائمة يصبح سطرًا واحدًا؛ تضيف الدالة فاصل السطر المناسب للنظام تلقائيًا بعد كل عنصر. يُنشأ الملف إذا لم يكن موجودًا شرط وجود المجلد الأب.
كتابة مصفوفة بايتات خام: Files.write مُحمَّلة أيضًا لقبول byte[]، وهو مفيد للبيانات الثنائية أو حين تمتلك البايتات مسبقًا:
كتابة سلسلة نصية كاملة دفعة واحدة (Java 11+):
أُضيفت Files.writeString في Java 11 كتسهيل للحالة الشائعة جدًا: كتابة String واحد. تعتمد UTF-8 افتراضيًا وهي أقصر طريق من قيمة نصية إلى ملف على القرص.
Files.write وFiles.writeString وسيطة Charset اختيارية. حذفها يعني UTF-8 (الافتراضي لدوال NIO.2) وهو آمن في الغالب — لكن التصريح به يوثّق نيّتك ويمنع المفاجآت على الأنظمة النادرة ذات الترميز الافتراضي المختلف.
الإلحاق مقابل الكتابة فوق: StandardOpenOption
بشكل افتراضي تُكتب Files.write وFiles.writeString فوق الملف بالكامل. مرّر StandardOpenOption لتغيير ذلك:
أبرز الخيارات التي ستستخدمها:
WRITE— فتح للكتابة (ضمني عند استدعاء دوال الكتابة).CREATE— أنشئ الملف إن لم يكن موجودًا، افتحه إن كان (السلوك الافتراضي).CREATE_NEW— أنشئ فقط؛ ارمِFileAlreadyExistsExceptionإن كان موجودًا. مفيد لضمان عدم الكتابة فوق ملف موجود.APPEND— انقل مؤشر الكتابة إلى النهاية قبل كل عملية كتابة. استخدمه معCREATEلبناء ملف سجلات بأمان.TRUNCATE_EXISTING— اقطع الملف إلى الصفر عند الفتح (هذا ما يجعل السلوك الافتراضي كتابةً فوق الملف).
APPEND في الوقت ذاته، فنداءات الكتابة الفردية ذرية على مستوى نظام التشغيل في معظم المنصات، لكن ترتيب الإدخالات غير محدد. للتسجيل متعدد الخيوط استخدم إطار تسجيل مخصصًا أو غلّف الكتابة بـ synchronized.
BufferedWriter: حين تحتاج إلى كتابة تدريجية
Files.write مثالية حين تمتلك البيانات كلها مسبقًا — فهي تجمّع المحتوى كله في الذاكرة قبل الكتابة. حين تحتاج إلى بناء الملف تدريجيًا (كتابة سطر سطر داخل حلقة مثلًا)، فـ BufferedWriter هي الأداة المناسبة. تتراكم الكتابات في مخزن مؤقت في الذاكرة (8 كيلوبايت افتراضيًا) وتُفرغ إلى القرص في قطع كبيرة كفؤة بدلًا من استدعاء النظام عند كل سطر.
ملاحظات مهمة:
Files.newBufferedWriterهو المصنع الخاص بـ NIO.2 — فضّله علىnew BufferedWriter(new FileWriter(...))لأنه يقبلPathوCharsetمباشرة.writer.newLine()يكتب فاصل السطر الخاص بالمنصة (\r\nعلى Windows،\nعلى Unix). تجنّب ترميز"\n"مباشرة في الملفات التي ستُقرأ على منصات متعددة.- كتلة try-with-resources تضمن تفريغ المخزن المؤقت وتحرير مقبض الملف حتى لو رُمي استثناء داخل الحلقة.
الإلحاق مع BufferedWriter
للإلحاق بدلًا من الكتابة فوق الملف، مرّر StandardOpenOption.APPEND إلى المصنع:
تمرير كل من CREATE وAPPEND معًا هو النمط الاصطلاحي: أنشئ الملف إن لم يكن موجودًا، وألحق به إن كان موجودًا.
اختيار الأداة المناسبة
إليك قاعدة عملية للقرار:
- محتوى صغير موجود في الذاكرة (سلسلة نصية، قائمة أسطر، مصفوفة بايتات) → استخدم
Files.writeStringأوFiles.write. سطر واحد، لا تدفقات تُدار. - محتوى كبير أو توليد تدريجي (حلقة، نتائج متدفقة، بناء تقرير سطرًا سطرًا) → استخدم
BufferedWriterعبرFiles.newBufferedWriter. المخزن المؤقت يمنع استدعاءات النظام الزائدة. - بيانات ثنائية → استخدم
Files.write(path, byte[])أوFiles.newOutputStreamمغلّفًا بـBufferedOutputStream.
BufferedWriter (مخزن 8 كيلوبايت) تتقلص هذه الاستدعاءات إلى 5–10 عمليات تفريغ تقريبًا، وهو أسرع بمراتب على الأقراص الدوّارة وأسرع ملحوظًا على أقراص SSD.
معالجة الاستثناءات
تُرمي جميع دوال الكتابة في NIO.2 IOException (استثناء محسوب). يجب إما التقاطه أو الإعلان عنه في توقيع الدالة. نمط بسيط لدالة مساعدة:
في كود التطبيق حيث لا يمكن تمرير الاستثناء المحسوب (مثلًا داخل تعبير lambda)، غلّفه باستثناء غير محسوب:
الخلاصة
استخدم Files.writeString للحالة الأبسط: سلسلة نصية واحدة، نداء واحد، UTF-8، انتهى. استخدم Files.write حين تمتلك قائمة أسطر أو مصفوفة بايتات. استخدم BufferedWriter من Files.newBufferedWriter حين تكتب تدريجيًا أو تهتم بالأداء على الحجم. تحكّم في الكتابة فوق الملف مقابل الإلحاق بـ StandardOpenOption. استخدم دائمًا try-with-resources لضمان إغلاق الملف وتفريغ المخزن المؤقت.