GagorAcadmey

تعلم برمجة تطبيقات الاندرويد من الصفر حتي الاحتراف

آخر المواضيع

السبت، 25 أغسطس 2018

التعامل مع الملفات



من المهم جدا معرفة كيفية قرأءة الملفات بمختلف انواعها وفي هذا المرحلة تهمنا فقط قرأءة التي توجد في بعض مجلدات الاندرويد أستوديو وسنتطرق الي االبقية لاحقا .
لغة جافا توفر لك الحزمة java.io و التي تحتوي على جميع الكلاسات التي تحتاجها للتعامل مع الملفات, سواء لإدخال بيانات جديدة في الملف Input Operation أو لإستخراج بيانات موجودة في الملف Output Operation, و حتى لإنشاء أو حذف الملفات. و من هنا جاء الإسم I/O. عملية إدخال البيانات أو إستخراجها من الملفات تسمى Streaming data, و هذه الكلمة تعني تدفق البيانات في اللغة العربية. سميت هذه العملية ( تدفق البيانات ) لأنك عندما تتعامل مع الملفات سواء للقراءة أو الكتابة فأنت تقوم بذلك على دفعات صغيرة, بمعنى أنك تبرمج على سبيل المثال برنامج يفتح ملف معين و يقرأ منه حرف واحد أو مجموعة أحرف في كل دفعة حتى نقل كامل بيانات الملف.

تحديد مسار الملفات التي تحاول التعامل معها

من الأشياء المهمة التي عليك الإنتباه لها عندما تتعامل مع الملفات هي معرفة كيف يتعامل نظام التشغيل الذي تبني برنامجك لأجله مع المسارات.

لنفترض أنه عندنا ملف موجود بداخل 3 مجلدات و نريد الوصول إليه كما في الصورة التالية:


الآن سترى كيف يتم تحديد المسار الموجود فيه الملف على أشهر أنظمة التشغيل.

  • على نظامي Mac و Windows نضع \\ بعد إسم كل مجلد عند تحديد المسار الموجود فيه الملف test.

  • على نظامي Unix و Linux نضع / بعد إسم كل مجلد عند تحديد المسار الموجود فيه الملف test.


لاحظ كيف أننا نضع نفس أسماء المجلدات و نفس إسم الملف لكي نصل للملف text. الإختلاف الوحيد هو الرمز الذي نضعه بعد كل مجلد.

A\\B\\C\\test.txt       // هكذا نصل للملف Mac و Windows على

A/B/C/test.txt          // هكذا نصل للملف Linux و Unix على
	

Character Streams

الكلاسات التي تصنف كـ Character Streams مصممة للتعامل مع الملفات النصية العادية التي تعتمد على الترميز unicode, أي كل حرف في الملف يتم تمثيله بـ 2 Bytes.

هناك العديد من الكلاسات التي تنتمي للـ Character Streams لكن الكلاسات الأكثر إستخداماً هما : FileReader و FileWriter.


تقنياً

الكلاسات FileReader و FileWriter يعتمدون على الكلاسات FileInputStream و FileOutputStream.
الفرق الوحيد بينهم هو أن الكلاس FileReader يقرأ 2 Bytes في كل مرة و الكلاس FileWriter يكتب 2 Bytes في كل مرة.

الترميز unicode يمثل كل حرف موجود في الملف بـ 2 Bytes.
إذاً الكلاس FileReader يقرأ حرف من الملف في كل مرة و الكلاس FileWriter يكتب حرف في الملف في كل مرة.


نكتب الكود التالي.

مثال

Main.java
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class Main {
 
    public static void main(String[] args) throws IOException {
 
        FileReader in = null;
        FileWriter out = null;
 
        try {
            in = new FileReader("C:\\MyFiles\\input.txt");
            out = new FileWriter("C:\\MyFiles\\ouput.txt");
            int c;
 
            while ((c = in.read()) != -1) {
                out.write(c);
            }
        }
        catch(IOException e) {
            System.out.println("There is IOException!");
        }
        finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
 
    }

}

شرح الكود

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

هنا قمنا باستدعاء الكلاسات التي سنحتاجها في البرنامج.


	public static void main(String[] args) throws IOException {

هنا قمنا بوضع الجملة throws IOException لأن الدالة close() التي قمنا باستدعائها لاحقاً في الكود قد ترمي إستثناء في الدالة main().


        FileReader in = null;
        FileWriter out = null;

هنا قمنا بإنشاء الكائن in لتمثيل الملف الذي سيتم فتحه لاحقاً.

و قمنا بإنشاء الكائن out لتمثيل الملف الذي سيتم خلقه لاحقاً.


            in = new FileReader("C:\\MyFiles\\input.txt");
            out = new FileWriter("C:\\MyFiles\\ouput.txt");
            int c;

هنا قمنا بتحديد الملف الذي سيمثله الكائن in.
الملف input.txt الموجود في المسار C:\\MyFiles\\ أصبح متمثلاً بالكائن in.

و قمنا بخلق ملف جديد على إسمه output.txt و يمثله الكائن out.
الملف output.txt حتى الآن عبارة عن ملف فارغ موجود في المسار C:\\MyFiles\\.

المتغير c سنستخدمه كوسيط بين الملفين عند نقل البيانات.


            while ((c = in.read()) != -1) {
                out.write(c);
            }

في الحلقة while سيحدث التالي في كل دورة:

  1. ستقوم الدالة read() بقراءة 2 Bytes من الملف الذي يمثله الكائن in ثم تخزنهم في المتغير c.

  2. إذا كانت قيمة المتغير c لا تساوي -1 , سيتم إدخال الـ 2 Bytes الموجودة في المتغير c في الكائن out بواسطة الدالة write().



الكلاسات المصممة للتعامل مع الـ Character Streams

في جافا تم بناء الكلاسات التي تتعامل مع الـ Character Streams بشكل متناسق ومن هذه الكلاسات .

الكلاس Reader هو الكلاس الأساسي لقراءة الأحرف من الملفات. و الكلاس Writer هو الكلاس الأساسي لكتابة الأحرف في الملفات.

أهم كلاسَين للتعامل مع الـ Character Streams هما الكلاس BufferedReader و الكلاس BufferedWriter بحيث يوفران لك كثير من الدوال التي تمكنك من التعامل مع الملفات بسهولة و مرونة و استخدام الـ buffer للحصول على أفضل أداء ممكن.


طريقة التعامل مع هذه الكلاسات

عندما تستخدم هذه الكلاسات ستستخدم مبدأ يسمى Upcasting.
و المقصود من هذا أنك مثلاً تنشئ كائن من الكلاس InputStream ثم تضع فيه أي كائن من الكلاسات التي ترث منه, مثل InputStreamReader.
بمعنى آخر تنشئ كائن من الكلاس InputStream ثم تحدد أنك تريد إستخدام الكلاس FileInputStream.


فكرة الـ Buffer و أهميتها عند التعامل مع الملفات

إفترض أن برنامجك سيقرأ على سبيل المثال ملف حجمه 10 MB, و التي تساوي 10,485,760 Bytes.

هل ستستدعي الدالة read() أكثر من 10 ملايين مرة حتى تقرأ هذا الملف؟!
حتماً لا لأنك إن فعلت ذلك ستهلك المعالج و الذاكرة و القرص الصلب لقراءة هذا الملف البسيط, و من هنا جائت فكرة الـ Buffer.


مفهوم الـ Buffer

الـ Buffer عبارة عن مساحة مؤقتة للتخزين يتم إنشاءها في الذاكرة من أجل قراءة كمية كبيرة من المعلومات, ثم يتم التخلص منها عند الإنتهاء.
عندما تستخدم كائن يتعامل مع الـ Buffer فهذا الكائن سينشئ هذه المساحة المؤقت في الذاكرة.

الآن إذا رجعنا للمثال السابق, كان يمكنك قراءة 1000 Bytes أو أكثر على سبيلا المثال في كل مرة تستدعي فيها الدالة read() بدل إستدعاءها لقراءة 1 Byte في كل مرة. و لو كان هذا مثال حي للاحظت الفارق في السرعة بين الأسلوبين.


في الجدول التالي وضعنا بعض كلاسات الـ Character Streams التي تستخدم للقراءة من الملفات.

الكلاس مع تعريفه
class InputStreamReader الكلاس InputStreamReader يقرأ بيانات الملف كـ bytes و يحولهم لأحرف حسب الترميز الذي نقوم بتحديده في الكونستركتور.
class FileReader الكلاس FileReader يرث من الكلاسات InputStreamReader و Reader و هو يستخدم لقراءة Streams من الأحرف.
class BufferedReader الكلاس BufferedReader يقرأ أحرف كائن الـ InputStreamReader و يضعهم في الـ buffer. و هذا يوفر لك طرق عديدة لقراءة المحتوى من الـ buffer. مثل قراءته حرفاً حرفاً, أو تخزينه في مصفوفة, أو قراءته سطراً سطراً.
يمكنك تحديد حجم الـ buffer, أو استخدام الحجم الإفتراضي لها و الذي يعتبر جيداً في معظم الحالات.

الكلاس FileReader في جافا

الكلاس FileReader يرث من الكلاسات InputStreamReader و Reader و هو يستخدم لقراءة Streams من الأحرف.


الكلاس FileReader يحتوي على الـ constructors المذكورين في الجدول التالي.

الكونستركتور مع تعريفه
public FileReader(File file) ينشئ كائن نوعه FileReader يستخدم للقراءة من كائن الـ File.
public FileReader(String fileName) ينشئ كائن نوعه FileReader يستخدم للقراءة من الملف الذي يشير إليه كائن الـ String.
public FileReader(FileDescriptor fd) ينشئ كائن نوعه FileReader يستخدم للقراءة من كائن الـ FileDescriptor.


بما أن الكلاس FileReader يرث من الكلاسات InputStreamReader و Reader, يمكنك إستدعاء الدوال التي سيرثها هذا الكائن من هذه الكلاسات.

مثال

في هذا المثال سنقوم بإنشاء ملف جديد و كتابة نص صغير فيه, ثم سنقوم بقراءة النص من الملف.

Main.java
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class Main {
 
    public static void main(String[] args) {
 
        try {
// لإنشاء ملف و الكتابة فيه لاحقاً FileWriter هنا قمنا بإنشاء كائن نوعه
            FileWriter fw = new FileWriter("C:\\Users\\EngAhmed\\Desktop\\index.txt");
 
// هنا قمنا بكتابة نص في الملف ثم قمنا بتفريغ الذاكرة و إغلاق الكائن المتصل بالملف
            fw.write("دروس تعلم برمجة تطبيقات الاندرويد من الصفر حتي الاحتراف");
            fw.flush();
            fw.close();
 
// يسمح بالقراءة من الملف fr يشير إلى الملف, إذا الكائن FileReader هنا قمنا بإنشاء كائن نوعه
            FileReader fr = new FileReader("C:\\Users\\EngAhmed\\Desktop\\index.txt");
 
// هنا قمنا بإنشاء حلقة تمر على جميع أحرف الملف, في كل دورة تقرأ حرف من الملف ثم تعرضه و بعدها قمنا بإغلاق الكائن المتصل بالملف
            int c;
            while ((c = fr.read()) != -1) {
                System.out.print((char)c);
            }
            fr.close();
        }
        catch(IOException e) {
            System.out.println("There is IOException!");
        }
 
    }
 
}

سنحصل على النتيجة التالية عند التشغيل.

دروس تعلم برمجة تطبيقات الاندرويد من الصفر حتي الاحتراف 

الكلاس BufferedReader في جافا

الكلاس BufferedReader يقرأ أحرف كائن الـ InputStreamReader و يضعهم في الـ buffer. و هذا يوفر لك طرق عديدة لقراءة المحتوى من الـ buffer. مثل قراءته حرفاً حرفاً, أو تخزينه في مصفوفة, أو قراءته سطراً سطراً.

يمكنك تحديد حجم الـ buffer, أو استخدام الحجم الإفتراضي لها و الذي يعتبر جيداً في معظم الحالات.


الكلاس BufferedReader يحتوي على الـ constructors المذكورين في الجدول التالي.

الكونستركتور مع تعريفه
BufferedReader(Reader in) ينشئ كائن نوعه BufferedReader يستخدم حجم الـ buffer الإفتراضي.
BufferedReader(Reader in, int size) ينشئ كائن نوعه BufferedReader يستخدم حجم الـ buffer الذي نقوم بتحديده.


بعد أن يصبح كائن الـ BufferedReader جاهزاً, يمكنك إستخدام الكثير من الدوال الجاهزة للقراءة من الملف أو لفعل أي شيء آخر.

الدالة مع تعريفها
public void close() throws IOException تغلق الـ BufferedReader و تقطع الإتصال بين الـ Stream و أي مصدر كان متصلاً فيه.
public int read() throws IOException في كل مرة تقرأ حرف جديد من أحرف الـ BufferedReader و ترجعه.
ترجع 1- إذا وصلت لنهاية الملف.
public int read(char[] cbuf, int offset, int length) throws IOException في كل مرة تستدعى فيها تقرأ مجموعة أحرف جديدة من الـ BufferedReader و تخزنهم في المصفوفة cbuf.
المتغير offset يقصد به من أي عنصر في المصفوفة cbuf سيكتب في الـ BufferedReader.
المتغير length يقصد به كم عنصر من عناصر المصفوفة cbuf سيكتب في الـ BufferedReader.
public String readLine() throws IOException في كل مرة تقرأ سطر جديد من أسطر الـ BufferedReader و ترجعه.
ترجع null إذا وصلت لنهاية الملف.
السطر يتعبر منتهياً إذا كان ينتهي بـ \n, أو بـ \r, أو بالإثنين مع بعض \n\r.
public long skip(long n) throws IOException تتخطى عدد من الأحرف الموجودة في الـ buffer. ثم ترجع عدد الأحرف التي تجاوزتها.
المتغير N يقصد به عدد الأحرف التي سيتم تجاوزها عند القراءة من الـ buffer.


مثال

في هذا المثال سنقوم بإنشاء ملف جديد و كتابة نص صغير فيه, ثم سنقوم بقراءة النص من الملف.

Main.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
 
public class Main {
 
    public static void main(String[] args) {
 
        try {
// يشير إلى الملف الذي سنكتب البيانات فيه و في حال لم يكن موجوداً, سيتم إنشاءه FileOutputStream هنا قمنا بإنشاء كائن نوعه
     FileOutputStream fos = new FileOutputStream("C:\\Users\\EngAhmed\\Desktop\\index2.txt");
 
// BufferedWriter من أجل كائن الـ buffer لوضعه في الـ OutputStreamWriter هنا قمنا بإنشاء كائن نوعه
     OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF-8" );
 
// للكتابة في الملف BufferedWriter هنا قمنا بإنشاء كائن نوعه
      BufferedWriter bw = new BufferedWriter(osw);
 
// هنا قمنا بكتابة نص في الملف ثم قمنا بتفريغ الذاكرة و إغلاق الكائن المتصل بالملف
            bw.write("مدونة الجقور لبرمجة تطبيقات الاندرويد");
            bw.newLine();
            bw.write("من الصفر وحتي الاحتراف");
            bw.flush();
            bw.close();
 
// يشير إلى الملف الذي ستقرأ البيانات منه FileInputStream هنا قمنا بإنشاء كائن نوعه
            FileInputStream fis = new FileInputStream("C:\\Users\\EngAhmed\\Desktop\\index.txt");
 
// BufferedReader من أجل كائن الـ buffer لوضعه في الـ InputStreamReader هنا قمنا بإنشاء كائن نوعه
            InputStreamReader isr = new InputStreamReader( fis, "UTF-8" );
 
// للقراءة من الملف BufferedReader هنا قمنا بإنشاء كائن نوعه
            BufferedReader br = new BufferedReader(isr);
 
// هنا قمنا بإنشاء حلقة تمر على جميع أحرف الملف, في كل دورة تقرأ حرف من الملف ثم تعرضه و بعدها قمنا بإغلاق الكائن المتصل بالملف
            int c;
            while ((c = br.read()) != -1) {
                System.out.print((char)c);
            }
            br.close();
        }
        catch(IOException e) {
            System.out.println("There is IOException!");
        }
 
    }
 
}

سنحصل على النتيجة التالية عند التشغيل.

مدونة الجقور لبرمجة تطبيقات الاندرويد
من الصفر وحتي الاحتراف

الكلاس InputStreamReader في جافا

الكلاس InputStreamReader يقرأ بيانات الملف كـ bytes و يحولهم لأحرف حسب الترميز الذي نقوم بتحديده في الكونستركتور.


الكلاس InputStreamReader يحتوي على الـ constructors المذكورين في الجدول التالي.

الكونستركتور مع تعريفه
public InputStreamReader(InputStream in) ينشئ كائن نوعه InputStreamReader يستخدم الترميز الإفتراضي.
public InputStreamReader(InputStream in, Charset cs) ينشئ كائن نوعه InputStreamReader يستخدم ترميز كائن الـ Charset
public InputStreamReader(InputStream in, CharsetDecoder dec) ينشئ كائن نوعه InputStreamReader يستخدم ترميز كائن الـ CharsetDecoder
public InputStreamReader(InputStream in, String charsetName) ينشئ كائن نوعه InputStreamReader يستخدم ترميز كائن الـ String


بعد أن يصبح كائن الـ InputStreamReader جاهزاً, يمكنك إستخدام الكثير من الدوال الجاهزة للقراءة من الملف أو لفعل أي شيء آخر.

الدالة مع تعريفها
public void close() throws IOException تغلق الـ InputStreamReader و تقطع الإتصال بين الـ Stream و أي مصدر كان متصلاً فيه.
public String getEncoding() ترجع إسم الترميز المستخدم من قبل كائن الـ InputStreamReader.
public int read() throws IOException في كل مرة تقرأ حرف جديد من أحرف الـ InputStreamReader و ترجعه.
ترجع 1- إذا وصلت لنهاية الملف.
public int read(char[] cbuf, int offset, int length) throws IOException في كل مرة تستدعى فيها تقرأ مجموعة أحرف جديدة من الـ InputStreamReader و تخزنهم في المصفوفة cbuf.
المتغير offset يقصد به من أي عنصر في المصفوفة cbuf سيكتب في الـ InputStreamReader.
المتغير length يقصد به كم عنصر من عناصر المصفوفة cbuf سيكتب في الـ InputStreamReader.


مثال

في هذا المثال سنقوم بإنشاء ملف جديد و كتابة نص صغير فيه, ثم سنقوم بقراءة النص من الملف.

Main.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
 
public class Main {
 
    public static void main(String[] args) {
 
        try {
// يشير إلى الملف الذي سنكتب البيانات فيه و في حال لم يكن موجوداً, سيتم إنشاءه FileOutputStream هنا قمنا بإنشاء كائن نوعه
      FileOutputStream fos = new FileOutputStream("C:\\Users\\EngAhmed\\Desktop\\index3.txt");
 
// للكتابة في الملف OutputStreamWriter هنا قمنا بإنشاء كائن نوعه
      OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF-8" );
 
// هنا قمنا بكتابة نص في الملف ثم قمنا بتفريغ الذاكرة و إغلاق الكائن المتصل بالملف
            osw.write("GagAcademy for Android");
            osw.flush();
            osw.close();
 
// يشير إلى الملف الذي ستقرأ البيانات منه FileInputStream هنا قمنا بإنشاء كائن نوعه
      FileInputStream fis = new FileInputStream("C:\\Users\\EngAhmed\\Desktop\\index3.txt");
 
// للقراءة من الملف InputStreamReader هنا قمنا بإنشاء كائن نوعه
      InputStreamReader isr = new InputStreamReader( fis, "UTF-8" );
 
// هنا قمنا بإنشاء حلقة تمر على جميع أحرف الملف, في كل دورة تقرأ حرف من الملف ثم تعرضه و بعدها قمنا بإغلاق الكائن المتصل بالملف
            int c;
            while ((c = isr.read()) != -1) {
                System.out.print((char)c);
            }
            isr.close();
        }
        catch(IOException e) {
            System.out.println("There is IOException!");
        }
 
    }
 
}

سنحصل على النتيجة التالية عند التشغيل.

GagAcademy for Android

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.

صفحتنا علي الفيسبوك

التسميات

المتواجدين حاليا