Skip to content

كيفية التعامل مع أنظمة الملفات المختلفة

يكشف Node.js عن العديد من ميزات أنظمة الملفات. لكن ليست كل أنظمة الملفات متشابهة. فيما يلي أفضل الممارسات المقترحة لإبقاء التعليمات البرمجية بسيطة وآمنة عند العمل مع أنظمة ملفات مختلفة.

سلوك نظام الملفات

قبل أن تتمكن من العمل مع نظام ملفات، تحتاج إلى معرفة كيفية عمله. تختلف أنظمة الملفات المختلفة في سلوكها، ولديها ميزات أكثر أو أقل من غيرها: حساسية حالة الأحرف، عدم حساسية حالة الأحرف، الحفاظ على حالة الأحرف، الحفاظ على شكل Unicode، دقة الطابع الزمني، السمات الموسعة، عُقد الملفات، أذونات Unix، تيارات البيانات البديلة، إلخ.

احذر من استنتاج سلوك نظام الملفات من process.platform. على سبيل المثال، لا تفترض أنه لأن برنامجك يعمل على Darwin فأنت تعمل بالتالي على نظام ملفات غير حساس لحالة الأحرف (HFS +)، حيث قد يستخدم المستخدم نظام ملفات حساس لحالة الأحرف (HFSX). وبالمثل، لا تفترض أنه لأن برنامجك يعمل على Linux فأنت تعمل بالتالي على نظام ملفات يدعم أذونات Unix وعُقد الملفات، حيث قد تكون على محرك أقراص خارجي معين أو محرك أقراص USB أو محرك أقراص شبكة لا يدعم ذلك.

قد لا يسهل نظام التشغيل استنتاج سلوك نظام الملفات، ولكن ليس كل شيء ضائع. بدلاً من الاحتفاظ بقائمة بكل أنظمة الملفات والسلوكيات المعروفة (التي ستكون دائمًا غير كاملة)، يمكنك اختبار نظام الملفات لمعرفة كيفية عمله بالفعل. غالبًا ما يكون وجود أو عدم وجود ميزات معينة يسهل اختبارها كافيًا لاستنتاج سلوك ميزات أخرى يصعب اختبارها.

تذكر أن بعض المستخدمين قد يكون لديهم أنظمة ملفات مختلفة مثبتة في مسارات مختلفة في شجرة العمل.

تجنب نهج أقل قاسم مشترك

قد يغريك جعل برنامجك يعمل مثل نظام ملفات أقل قاسم مشترك، عن طريق تطبيع جميع أسماء الملفات إلى الأحرف الكبيرة، وتطبيع جميع أسماء الملفات إلى نموذج Unicode NFC، وتطبيع جميع طوابع الوقت إلى دقة 1 ثانية على سبيل المثال. سيكون هذا هو نهج أقل قاسم مشترك.

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

ماذا يحدث عندما تحتاج لاحقًا إلى دعم نظام ملفات لديه دقة طابع زمني تبلغ 2 ثانية أو 24 ساعة فقط؟ ماذا يحدث عندما يتقدم معيار Unicode ليشمل خوارزمية تطبيع مختلفة قليلاً (كما حدث في الماضي)؟

يميل نهج أقل قاسم مشترك إلى محاولة إنشاء برنامج محمول باستخدام مكالمات نظام "محمولة" فقط. هذا يؤدي إلى برامج مُتسربة وليست قابلة للنقل في الواقع.

اتباع نهج المجموعة الكاملة

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

تعامل مع أنظمة الملفات المختلفة من خلال جعل برنامجك يعمل كنسخة أكثر تقدمًا من نظام الملفات. دعم مجموعة كاملة من جميع الميزات الممكنة: حساسية حالة الأحرف، وحفظ حالة الأحرف، وحساسية نموذج Unicode، وحفظ نموذج Unicode، وأذونات Unix، وعلامات زمنية نانوثانية عالية الدقة، والسمات الموسعة وما إلى ذلك.

بمجرد أن يكون لديك حفظ حالة الأحرف في برنامجك، يمكنك دائمًا تنفيذ عدم حساسية حالة الأحرف إذا كنت بحاجة إلى التفاعل مع نظام ملفات غير حساس لحالة الأحرف. ولكن إذا تخليت عن حفظ حالة الأحرف في برنامجك، فلن تتمكن من التفاعل بأمان مع نظام ملفات يحفظ حالة الأحرف. وينطبق الشيء نفسه على حفظ نموذج Unicode وحفظ دقة الطابع الزمني.

إذا قدم لك نظام الملفات اسم ملف ممزوج بحروف كبيرة وصغيرة، فاحتفظ باسم الملف في الحالة المحددة بالضبط. إذا قدم لك نظام الملفات اسم ملفًا بنموذج Unicode مختلط أو NFC أو NFD (أو NFKC أو NFKD)، فاحتفظ باسم الملف في تسلسل البايت المحدد بالضبط. إذا قدم لك نظام الملفات طابعًا زمنيًا بالميللي ثانية، فاحتفظ بالطابع الزمني بدقة الميللي ثانية.

عندما تعمل مع نظام ملفات أقل، يمكنك دائمًا أخذ عينات فرعية بشكل مناسب، مع وظائف المقارنة حسبما هو مطلوب من خلال سلوك نظام الملفات الذي يعمل عليه برنامجك. إذا كنت تعلم أن نظام الملفات لا يدعم أذونات Unix، فلا يجب أن تتوقع قراءة أذونات Unix نفسها التي تكتبها. إذا كنت تعلم أن نظام الملفات لا يحفظ حالة الأحرف، فيجب أن تكون مستعدًا لرؤية ABC في قائمة الدليل عندما يقوم برنامجك بإنشاء abc. ولكن إذا كنت تعلم أن نظام الملفات يحفظ حالة الأحرف، فيجب أن تعتبر ABC اسم ملف مختلف عن abc، عند الكشف عن إعادة تسمية الملفات أو إذا كان نظام الملفات حساسًا لحالة الأحرف.

الحفاظ على حالة الأحرف

قد تقوم بإنشاء دليل باسم test /abc وتندهش لرؤية أن fs.readdir('test') يعيد ['ABC'] في بعض الأحيان. هذا ليس خطأ في Node. تعيد Node اسم الملف كما يخزنه نظام الملفات، وليس كل أنظمة الملفات تدعم الحفاظ على حالة الأحرف. تقوم بعض أنظمة الملفات بتحويل جميع أسماء الملفات إلى أحرف كبيرة (أو صغيرة).

الحفاظ على شكل يونيكود

يُشبه مفهوم الحفاظ على حالة الأحرف مفهوم الحفاظ على شكل يونيكود. لفهم سبب وجوب الحفاظ على شكل يونيكود، تأكد أولاً من فهمك لسبب وجوب الحفاظ على حالة الأحرف. إن الحفاظ على شكل يونيكود بسيط بنفس القدر عند فهمه بشكل صحيح. يمكن لليونيكود ترميز نفس الأحرف باستخدام العديد من تسلسلات البايت المختلفة. قد تبدو العديد من السلاسل متشابهة، ولكن لها تسلسلات بايت مختلفة. عند العمل مع سلاسل UTF-8، انتبه إلى أن تتوافق توقعاتك مع طريقة عمل يونيكود. تمامًا كما لا تتوقع أن يتم ترميز جميع أحرف UTF-8 إلى بايت واحد، فلا يجب أن تتوقع أن يكون لعدة سلاسل UTF-8 تبدو متشابهة للعين البشرية نفس التمثيل البايتی. قد يكون هذا توقعًا يمكنك امتلاكه لـ ASCll، ولكن ليس لـ UTF-8.

قد تقوم بإنشاء دليل باسم test/ café (شكل يونيكود NFC مع تسلسل البايت <63 61 66 c3 a9> و string.length ===5) وتندهش لرؤية أن fs.readdir('test') يعيد ['café'] في بعض الأحيان (شكل يونيكود NFD مع تسلسل البايت <63 61 66 65 cc 81> و string.length ===6). هذا ليس خطأ في Node. تعيد Node.js اسم الملف كما يخزنه نظام الملفات، وليس كل أنظمة الملفات تدعم الحفاظ على شكل يونيكود. HFS+، على سبيل المثال، سيقوم بتوحيد جميع أسماء الملفات إلى شكل يكون دائمًا تقريبًا هو نفسه شكل NFD. لا تتوقع أن يتصرف HFS+ بنفس طريقة NTFS أو EXT 4 والعكس صحيح. لا تحاول تغيير البيانات بشكل دائم من خلال التوحيد كطبقة تجريد مسربة لتغطية اختلافات يونيكود بين أنظمة الملفات. هذا سيخلق مشاكل دون حل أي منها. بدلاً من ذلك، حافظ على شكل يونيكود واستخدم التوحيد كدالة مقارنة فقط.

عدم حساسية نموذج يونيكود

يُعد عدم حساسية نموذج يونيكود والحفاظ على نموذج يونيكود سلوكين مختلفين لنظام الملفات، وغالباً ما يتم الخلط بينهما. تمامًا كما تم تطبيق عدم حساسية الأحرف الكبيرة والصغيرة بشكل غير صحيح في بعض الأحيان من خلال تطبيع أسماء الملفات بشكل دائم إلى أحرف كبيرة عند تخزين أسماء الملفات ونقلها، فقد تم تطبيق عدم حساسية نموذج يونيكود بشكل غير صحيح في بعض الأحيان من خلال تطبيع أسماء الملفات بشكل دائم إلى نموذج يونيكود معين (NFD في حالة HFS +) عند تخزين أسماء الملفات ونقلها. من الممكن، والأفضل بكثير، تطبيق عدم حساسية نموذج يونيكود دون التضحية بالحفاظ على نموذج يونيكود، وذلك باستخدام تطبيع يونيكود للمقارنة فقط.

مقارنة نماذج يونيكود المختلفة

يوفر Node.js string.normalize ('NFC' / 'NFD') الذي يمكنك استخدامه لتطبيع سلسلة UTF-8 إلى NFC أو NFD. يجب ألا تخزن أبدًا الإخراج من هذه الدالة، بل تستخدمها فقط كجزء من دالة مقارنة لاختبار ما إذا كانت سلسلتي UTF-8 ستبدوان متطابقتين للمستخدم. يمكنك استخدام string1.normalize('NFC')=== string2.normalize('NFC') أو string1.normalize('NFD')=== string2.normalize('NFD') كدالة مقارنة لك. لا يهم النموذج الذي تستخدمه.

التطبيع سريع، لكنك قد ترغب في استخدام ذاكرة تخزين مؤقت كمدخل لدالة المقارنة الخاصة بك لتجنب تطبيع نفس السلسلة عدة مرات. إذا لم تكن السلسلة موجودة في ذاكرة التخزين المؤقت، فقم بتطبيعها وتخزينها مؤقتًا. احرص على عدم تخزين ذاكرة التخزين المؤقت أو إبقائها، استخدمها فقط كذاكرة تخزين مؤقتة.

لاحظ أن استخدام normalize () يتطلب أن يتضمن إصدار Node.js الخاص بك ICU (وإلا فإن normalize () سيرجع السلسلة الأصلية فقط). إذا قمت بتنزيل أحدث إصدار من Node.js من الموقع الإلكتروني، فسوف يتضمن ICU.

دقة الطابع الزمني

قد تقوم بتعيين mtime (وقت التعديل) للملف إلى 1444291759414 (دقة الميلي ثانية) وتفاجأ برؤية أن fs.stat يعيد mtime الجديد كـ 1444291759000 (دقة ثانية واحدة) أو 1444291758000 (دقة ثانيتين). هذا ليس خطأ في Node. يعيد Node.js الطابع الزمني كما يخزنه نظام الملفات، ولا تدعم جميع أنظمة الملفات دقة الطابع الزمني للنانوثانية أو الميلي ثانية أو ثانية واحدة. بعض أنظمة الملفات لديها دقة خشنة جدًا لطابع زمني atime على وجه الخصوص، على سبيل المثال، 24 ساعة لبعض أنظمة ملفات FAT.

لا تُفسد أسماء الملفات والطوابع الزمنية من خلال التطبيع

أسماء الملفات والطوابع الزمنية هي بيانات المستخدم. تمامًا كما أنك لن تعيد كتابة بيانات ملفات المستخدم تلقائيًا لتحويل البيانات إلى أحرف كبيرة أو تطبيع نهايات الأسطر CRLF إلى LF، فلا يجب عليك أبدًا تغيير أو التدخل في أو إفساد أسماء الملفات أو الطوابع الزمنية من خلال حالة الأحرف / شكل Unicode / تطبيع الطابع الزمني. يجب استخدام التطبيع فقط للمقارنة، وليس أبدًا لتغيير البيانات.

التطبيع هو في الواقع رمز هاش ضيّاع. يمكنك استخدامه لاختبار أنواع معينة من التكافؤ (مثل: هل تبدو عدة سلاسل متشابهة على الرغم من اختلاف تسلسلات البايت الخاصة بها) ولكن لا يمكنك أبدًا استخدامه كبديل للبيانات الفعلية. يجب أن يمرر برنامجك بيانات اسم الملف والطابع الزمني كما هي.

يمكن لبرنامجك إنشاء بيانات جديدة في NFC (أو في أي مجموعة من أشكال Unicode يفضلها) أو باستخدام اسم ملف صغير أو كبير الحروف، أو باستخدام طابع زمني بدقة ثانيتين، لكن برنامجك يجب ألا يُفسد بيانات المستخدم الموجودة عن طريق فرض حالة الأحرف / شكل Unicode / تطبيع الطابع الزمني. بدلاً من ذلك، اتبع نهجًا شاملًا واحفظ حالة الأحرف وشكل Unicode ودقة الطابع الزمني في برنامجك. وبهذه الطريقة، ستتمكن من التفاعل بأمان مع أنظمة الملفات التي تفعل الشيء نفسه.

استخدم دوال مقارنة التطبيع بشكل مناسب

تأكد من استخدام دوال مقارنة حالة الأحرف / شكل Unicode / الطابع الزمني بشكل مناسب. لا تستخدم دالة مقارنة اسم ملف غير حساسة لحالة الأحرف إذا كنت تعمل على نظام ملفات حساس لحالة الأحرف. لا تستخدم دالة مقارنة غير حساسة لشكل Unicode إذا كنت تعمل على نظام ملفات حساس لشكل Unicode (مثل NTFS ومعظم أنظمة ملفات Linux التي تحفظ كل من NFC و NFD أو أشكال Unicode مختلطة). لا تقارن الطوابع الزمنية بدقة ثانيتين إذا كنت تعمل على نظام ملفات بدقة طابع زمني نانوثانية.

كن مستعدًا لاختلافات طفيفة في دوال المقارنة

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

على سبيل المثال، يقوم نظام Apple's HFS+ بتطبيع أسماء الملفات إلى نموذج NFD ولكن هذا النموذج NFD هو في الواقع إصدار أقدم من النموذج NFD الحالي وقد يختلف أحيانًا قليلاً عن نموذج NFD الخاص بأحدث معيار Unicode. لا تتوقع أن يكون HFS+ NFD هو نفسه Unicode NFD تمامًا في كل وقت.