كيفية التعامل مع أنظمة الملفات المختلفة
تعرض Node.js العديد من ميزات أنظمة الملفات. ولكن ليست كل أنظمة الملفات متشابهة. فيما يلي أفضل الممارسات المقترحة للحفاظ على بساطة التعليمات البرمجية الخاصة بك وسلامتها عند العمل مع أنظمة ملفات مختلفة.
سلوك نظام الملفات
قبل أن تتمكن من العمل مع نظام ملفات، تحتاج إلى معرفة كيفية تصرفه. تتصرف أنظمة الملفات المختلفة بشكل مختلف ولديها ميزات أكثر أو أقل من غيرها: حساسية حالة الأحرف، عدم حساسية حالة الأحرف، الحفاظ على حالة الأحرف، الحفاظ على نموذج Unicode، دقة الطابع الزمني، السمات الموسعة، العقد، أذونات Unix، تدفقات البيانات البديلة، إلخ.
كن حذرًا من استنتاج سلوك نظام الملفات من process.platform
. على سبيل المثال، لا تفترض أنه نظرًا لأن برنامجك يعمل على Darwin، فأنت تعمل بالتالي على نظام ملفات غير حساس لحالة الأحرف (HFS+)، حيث قد يستخدم المستخدم نظام ملفات حساس لحالة الأحرف (HFSX). وبالمثل، لا تفترض أنه نظرًا لأن برنامجك يعمل على Linux، فأنت تعمل بالتالي على نظام ملفات يدعم أذونات Unix والعقد، حيث قد تكون على محرك أقراص خارجي أو USB أو محرك أقراص شبكة معين لا يدعم ذلك.
قد لا يجعل نظام التشغيل من السهل استنتاج سلوك نظام الملفات، ولكن لم يفت الأوان. بدلاً من الاحتفاظ بقائمة بكل نظام ملفات وسلوك معروف (والذي سيكون دائمًا غير مكتمل)، يمكنك فحص نظام الملفات لمعرفة كيف يتصرف بالفعل. غالبًا ما يكون وجود أو عدم وجود بعض الميزات التي يسهل فحصها كافيًا لاستنتاج سلوك الميزات الأخرى التي يصعب فحصها.
تذكر أن بعض المستخدمين قد قاموا بتركيب أنظمة ملفات مختلفة في مسارات مختلفة في شجرة العمل.
تجنب اتباع نهج القاسم المشترك الأدنى
قد تميل إلى جعل برنامجك يتصرف مثل نظام ملفات القاسم المشترك الأدنى، عن طريق تطبيع جميع أسماء الملفات إلى أحرف كبيرة، وتطبيع جميع أسماء الملفات إلى نموذج NFC Unicode، وتطبيع جميع الطوابع الزمنية للملفات إلى دقة ثانية واحدة على سبيل المثال. سيكون هذا هو نهج القاسم المشترك الأدنى.
لا تفعل هذا. لن تتمكن إلا من التفاعل بأمان مع نظام ملفات لديه نفس خصائص القاسم المشترك الأدنى بالضبط في كل جانب. لن تتمكن من العمل مع أنظمة الملفات الأكثر تقدمًا بالطريقة التي يتوقعها المستخدمون، وستواجه تصادمات في أسماء الملفات أو الطوابع الزمنية. ستفقد بالتأكيد بيانات المستخدم وتتلفها من خلال سلسلة من الأحداث التابعة المعقدة، وستنشئ أخطاء يصعب إن لم يكن من المستحيل حلها.
ماذا يحدث عندما تحتاج لاحقًا إلى دعم نظام ملفات بدقة طابع زمني تبلغ ثانيتين أو 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 اسم الملف كما يخزنه نظام الملفات، ولا تدعم جميع أنظمة الملفات الحفاظ على حالة الأحرف. تقوم بعض أنظمة الملفات بتحويل جميع أسماء الملفات إلى أحرف كبيرة (أو صغيرة).
الحفاظ على شكل Unicode
يُعد الحفاظ على حالة الأحرف والحفاظ على شكل Unicode مفاهيم متشابهة. لفهم سبب ضرورة الحفاظ على شكل Unicode، تأكد أولاً من أنك تفهم سبب ضرورة الحفاظ على حالة الأحرف. الحفاظ على شكل Unicode بسيط تمامًا عند فهمه بشكل صحيح. يمكن لـ Unicode ترميز الأحرف نفسها باستخدام عدة تسلسلات بايت مختلفة. قد تبدو عدة سلاسل متشابهة، ولكن لها تسلسلات بايت مختلفة. عند العمل مع سلاسل UTF-8، كن حذرًا من أن توقعاتك تتوافق مع طريقة عمل Unicode. تمامًا كما لا تتوقع ترميز جميع أحرف UTF-8 إلى بايت واحد، يجب ألا تتوقع أن يكون للعديد من سلاسل UTF-8 التي تبدو متشابهة للعين البشرية نفس تمثيل البايت. قد يكون هذا توقعًا يمكنك الحصول عليه من ASCll، ولكن ليس من UTF-8.
قد تقوم بإنشاء دليل يسمى test/ café
(شكل NFC Unicode مع تسلسل البايت <63 61 66 c3 a9>
و string.length ===5
) وتتفاجأ أحيانًا برؤية أن fs.readdir('test')
تُرجع ['café']
(شكل NFD Unicode مع تسلسل البايت <63 61 66 65 cc 81>
و string.length ===6
). هذا ليس خطأ في Node. يُرجع Node.js اسم الملف كما يخزنه نظام الملفات، ولا تدعم جميع أنظمة الملفات الحفاظ على شكل Unicode. على سبيل المثال، سيقوم HFS+ بتطبيع جميع أسماء الملفات إلى شكل هو دائمًا تقريبًا نفس شكل NFD. لا تتوقع أن يتصرف HFS+ بنفس طريقة NTFS أو EXT 4 والعكس صحيح. لا تحاول تغيير البيانات بشكل دائم من خلال التطبيع كطريقة تجريد تسرب لتغطية الاختلافات بين أنظمة الملفات في Unicode. سيؤدي ذلك إلى خلق مشاكل دون حل أي منها. بدلاً من ذلك، حافظ على شكل Unicode واستخدم التطبيع كوظيفة مقارنة فقط.
عدم حساسية نموذج يونيكود
عدم حساسية نموذج يونيكود والحفاظ على نموذج يونيكود هما سلوكان مختلفان لنظام الملفات غالبًا ما يتم الخلط بينهما. تمامًا كما تم تنفيذ عدم حساسية حالة الأحرف أحيانًا بشكل غير صحيح عن طريق تطبيع أسماء الملفات بشكل دائم إلى أحرف كبيرة عند تخزين ونقل أسماء الملفات، كذلك تم تنفيذ عدم حساسية نموذج يونيكود أحيانًا بشكل غير صحيح عن طريق تطبيع أسماء الملفات بشكل دائم إلى نموذج يونيكود معين (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 التي يفضلها) أو باسم ملف صغير أو كبير، أو بطابع زمني بدقة 2 ثانية، ولكن يجب ألا يقوم برنامجك بإتلاف بيانات المستخدم الحالية عن طريق فرض حالة الأحرف / نموذج Unicode / تسوية الطوابع الزمنية. بدلاً من ذلك، اعتمد نهج المجموعة الفائقة واحتفظ بحالة الأحرف ونموذج Unicode ودقة الطابع الزمني في برنامجك. بهذه الطريقة، ستتمكن من التفاعل بأمان مع أنظمة الملفات التي تفعل الشيء نفسه.
استخدم وظائف مقارنة التسوية بشكل مناسب
تأكد من أنك تستخدم وظائف مقارنة حالة الأحرف / نموذج Unicode / الطابع الزمني بشكل مناسب. لا تستخدم وظيفة مقارنة اسم ملف غير حساسة لحالة الأحرف إذا كنت تعمل على نظام ملفات حساس لحالة الأحرف. لا تستخدم وظيفة مقارنة غير حساسة لنموذج Unicode إذا كنت تعمل على نظام ملفات حساس لنموذج Unicode (على سبيل المثال، NTFS ومعظم أنظمة ملفات Linux التي تحتفظ بكل من NFC و NFD أو نماذج Unicode المختلطة). لا تقارن الطوابع الزمنية بدقة 2 ثانية إذا كنت تعمل على نظام ملفات بدقة طابع زمني نانو ثانية.
كن مستعدًا للاختلافات الطفيفة في وظائف المقارنة
كن حذرًا من أن وظائف المقارنة الخاصة بك تتطابق مع وظائف نظام الملفات (أو تحقق من نظام الملفات إذا كان ذلك ممكنًا لمعرفة كيف تتم المقارنة بالفعل). على سبيل المثال، عدم حساسية حالة الأحرف أكثر تعقيدًا من مقارنة toLowerCase()
بسيطة. في الواقع، عادةً ما يكون toUpperCase()
أفضل من toLowerCase()
(لأنه يعالج بعض أحرف اللغات الأجنبية بشكل مختلف). ولكن الأفضل من ذلك هو التحقق من نظام الملفات لأن كل نظام ملفات لديه جدول مقارنة حالة الأحرف الخاص به المدمج.
على سبيل المثال، يقوم HFS+ الخاص بـ Apple بتطبيع أسماء الملفات إلى نموذج NFD ولكن نموذج NFD هذا هو في الواقع إصدار أقدم من نموذج NFD الحالي وقد يختلف أحيانًا اختلافًا طفيفًا عن نموذج NFD الخاص بأحدث معيار Unicode. لا تتوقع أن يكون HFS+ NFD هو نفسه تمامًا Unicode NFD طوال الوقت.