هل تعلم أنه من الممكن أن يكون هناك ملف موجود وغير موجود في نفس الوقت؟ هل تعلم أنه يمكنك حذف ملف والاستمرار في استخدامه؟ اكتشف هذه الحالات وغيرها من حالات حافة الملفات في تطوير البرامج.
في مقالتي السابقة حول الحالات المتطورة في تطوير البرمجيات، كنت أكتب عن مصائد النص وقدمت لك بعض الاقتراحات حول كيفية تجنبها. في منشور المدونة هذا، أود التركيز على الملفات وعمليات إدخال/إخراج الملفات.
توفر واجهة برمجة تطبيقات java.io.File، من بين طرق أخرى، هذه الطرق الثلاث:
#exists()
#isDirectory()
#isFile()
قد يعتقد المرء أنه إذا تمت الإشارة إليه بواسطة مسار معين موجود، فإن الكائن يكون إما ملفًا أو دليلًا - كما هو الحال في هذا السؤال في Stack Overflow. ومع ذلك، هذا ليس صحيحا دائما.
لم يتم ذكره صراحةً في File#isFile() javadocs، ولكن الملف ** هناك يعني حقًا ** ملف عادي. وبالتالي، قد توجد ملفات يونكس خاصة مثل الأجهزة والمقابس والأنابيب ولكنها ليست ملفات في هذا التعريف.
انظر المقتطف التالي:
import java.io.File val file = File("/dev/null") println("exists: ${file.exists()}") println("isFile: ${file.isFile()}") println("isDirectory: ${file.isDirectory()}")
كما ترون في العرض التوضيحي المباشر، قد يكون هناك ملف ليس ملفًا ولا دليلًا.
الروابط الرمزية هي أيضًا ملفات خاصة ولكن يتم التعامل معها بشفافية في كل مكان تقريبًا في واجهة برمجة تطبيقات java.io (القديمة). الاستثناء الوحيد هو عائلة الأساليب #getCanonicalPath()/#getCanonicalFile(). الشفافية هنا تعني أن جميع العمليات يتم توجيهها إلى الهدف، مثلما يتم تنفيذها مباشرة عليه. عادة ما تكون هذه الشفافية مفيدة، على سبيل المثال. يمكنك فقط القراءة من بعض الملفات أو الكتابة إليها. لا تهتم بدقة مسار الارتباط الاختياري. ومع ذلك، فإنه قد يؤدي أيضا إلى بعض الحالات الغريبة. على سبيل المثال، قد يكون هناك ملف موجود وغير موجود في نفس الوقت.
دعونا نفكر في رابط رمزي متدلي. الهدف غير موجود، لذا فإن جميع الطرق من القسم السابق ستعيد خطأ. ومع ذلك، فإن مسار الملف المصدر لا يزال مشغولاً، على سبيل المثال. لا يمكنك إنشاء ملف جديد على هذا المسار. إليك الكود الذي يوضح هذه الحالة:
import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths val path = Paths.get("foo") Files.createSymbolicLink(path, path) println("exists : ${path.toFile().exists()}") println("isFile : ${path.toFile().isFile()}") println("isDirectory : ${path.toFile().isDirectory()}") println("createNewFile: ${path.toFile().createNewFile()}")
والعرض التجريبي المباشر.
في java.io API، لإنشاء دليل ربما يكون غير موجود والتأكد من وجوده بعد ذلك، يمكنك استخدام File#mkdir() (أو File#mkdirs() إذا كنت تريد إنشاء أدلة أصل غير موجودة كـ حسنًا) ثم File#isDirectory(). ومن المهم استخدام هذه الطرق بالترتيب المذكور. دعونا نرى ما قد يحدث إذا تم عكس الطلب. هناك حاجة إلى خيطين (أو أكثر) يقومان بنفس العمليات لتوضيح هذه الحالة. هنا سنستخدم الخيوط الزرقاء والحمراء.
(أحمر) هل الدليل ()؟ — لا، بحاجة إلى إنشاء
(أزرق) هل الدليل ()؟ — لا، بحاجة إلى إنشاء
(أحمر) mkdir()؟ - نجاح
(أزرق) مكدير ()؟ - يفشل
كما ترون فشل الخيط الأزرق في إنشاء دليل. ومع ذلك، فقد تم إنشاؤه بالفعل، لذا يجب أن تكون النتيجة إيجابية. إذا تم استدعاء isDirectory() في النهاية، لكانت النتيجة صحيحة دائمًا.
يقتصر عدد الملفات المفتوحة في نفس الوقت بواسطة عملية UNIX معينة على قيمة RLIMIT_NOFILE. على Android، يكون هذا عادةً 1024 ولكن بشكل فعال (باستثناء واصفات الملفات المستخدمة بواسطة إطار العمل) يمكنك استخدام أقل من ذلك (أثناء الاختبارات مع نشاط فارغ على Android 8.0.0، كان هناك ما يقرب من 970 واصفًا للملفات متاحة للاستخدام). ماذا يحدث إذا حاولت فتح المزيد؟ حسنًا، لن يتم فتح الملف. اعتمادًا على السياق، قد تواجه استثناءً لسبب واضح (عدد كبير جدًا من الملفات المفتوحة)، وقليل من الرسالة الغامضة (على سبيل المثال، لا يمكن فتح هذا الملف كواصف ملف؛ من المحتمل أن تكون مضغوطة) أو مجرد خطأ كقيمة إرجاع عندما تتوقع عادةً أنها صحيحة. راجع الكود الذي يوضح هذه المشكلات:
package pl.droidsonroids.edgetest import android.content.res.AssetFileDescriptor import android.support.test.InstrumentationRegistry import org.junit.Assert import org.junit.Test class TooManyOpenFilesTest { //asset named "test" required @Test fun tooManyOpenFilesDemo() { val context = InstrumentationRegistry.getContext() val assets = context.assets val descriptors = mutableListOf() try { for (i in 0..1024) { descriptors.add(assets.openFd("test")) } } catch (e: Exception) { e.printStackTrace() //java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed } try { context.openFileOutput("test", 0) } catch (e: Exception) { e.printStackTrace() //java.io.FileNotFoundException: /data/user/0/pl.droidsonroids.edgetest/files/test (Too many open files) } val sharedPreferences = context.getSharedPreferences("test", 0) Assert.assertTrue(sharedPreferences.edit().putBoolean("test", true).commit()) } }
لاحظ أنه إذا كنت تستخدم #apply()، فلن يتم حفظ القيمة باستمرار - لذلك لن تحصل على أي استثناء. ومع ذلك، سيكون من الممكن الوصول إليه حتى يتم إنهاء عملية التطبيق التي تحتفظ بمثيل SharedPreferences. وذلك لأن التفضيلات المشتركة يتم حفظها أيضًا في الذاكرة.
قد يظن المرء أن الزومبي والغيلان والمخلوقات المماثلة الأخرى موجودة في خيال الخيال والرعب فقط. لكن...إنها حقيقية في علوم الكمبيوتر! تشير هذه المصطلحات الشائعة إلى عمليات الزومبي. في الواقع، يمكن أيضًا إنشاء ملفات أوندد بسهولة.
في أنظمة التشغيل المشابهة لـ Unix، يتم عادةً حذف الملفات عن طريق إلغاء الارتباط. تتم إزالة اسم الملف غير المرتبط من نظام الملفات (على افتراض أنه الارتباط الثابت الأخير) ولكن تظل أي واصفات ملف مفتوحة بالفعل صالحة وقابلة للاستخدام. لا يزال بإمكانك القراءة من هذا الملف والكتابة إليه. وإليكم المقطع:
import java.io.BufferedReader import java.io.File import java.io.FileReader val file = File("test") file.writeText("this is file content") BufferedReader(FileReader(file)).use { println("deleted?: ${file.delete()}") println("content?: ${it.readLine()}") }
والعرض التجريبي المباشر.
أولاً، تذكر أنه لا يمكننا أن ننسى الطريقة الصحيحة لاستدعاء الطلب عند إنشاء أدلة غير موجودة. علاوة على ذلك، ضع في اعتبارك أن عدد الملفات المفتوحة في نفس الوقت محدود ولا يتم احتساب الملفات التي تفتحها صراحةً فقط. وأخيرًا وليس آخرًا، فإن خدعة حذف الملفات قبل الاستخدام الأخير يمكن أن تمنحك المزيد من المرونة.
نشرت في الأصل على www.thedroidsonroids.com في 27 سبتمبر 2017.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3