توفّر Elixir صياغة أنيقة للتعامل مع البيانات الثنائية، كما رأينا مع الصيغة الخاصة <<>> المخصّصة للتعامل مع السلاسل البتية.
النوع الثنائي هو تخصيص لنوع السلاسل البتية. فبينما يمكن أن تكون السلاسل البتية بأي طول (أي عدد من البتات)، فإن الثنائيات هي التي يمكن فيها قسمة عدد البتات على 8 دون باقٍ. أي أننا عند التعامل مع الثنائيات كثيرًا ما نفكّر في الأمور من حيث البايتات (8 بتات). ويمكن للبايت أن يمثّل أعدادًا صحيحة من 0 إلى 255. ومن الشائع التعامل مع قيم البايتات في النظام الست عشري، 0x00 - 0xFF.
تُعرَّف القيم الحرفية الثنائية باستخدام الصيغة الخاصة للسلاسل البتية <<>>. وعند تعريف قيمة حرفية ثنائية، يمكننا استخدام أعداد صحيحة وسلاسل نصية حرفية. الأعداد الصحيحة الأكبر من 255 ستتجاوز الحد، ولن يُستخدم منها إلا آخر 8 بتات. وبشكل افتراضي، يُطبَّق المعدّل ::binary على القيمة. ويمكننا دمج الثنائيات باستخدام العامل <>/2.
<<255>> == <<0xFF>>
# Overflowing bits are truncated
<<256>> == <<0>>
<<2, 4, 6, 8, 10, 12, 14, 16>> == <<0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10>>
البايت الصفري هو اسم آخر لـ <<0>>.
تمتد مطابقة الأنماط كذلك إلى الثنائيات، ويمكننا مطابقة نمط جزء من البيانات الثنائية تمامًا كما نفعل مع المصفوفة.
# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>
وكما هو الحال مع الأنواع الأخرى من مطابقة الأنماط، يمكننا استخدام هذا في توقيعات الدوال للمطابقة عند الاختيار من بين عدة فروع للدالة.
كنت تعمل على مشروع يتيح للمستخدمين رفع الملفات إلى الخادم لمشاركتها مع مستخدمين آخرين. وقد أُوكلت إليك مهمة كتابة دالة للتحقق من أن الملف المرفوع يطابق نوع الوسائط الخاص به. وبعد بعض البحث، تكتشف أن البايتات القليلة الأولى من الملف تكون عادةً فريدة لهذا النوع من الملفات، مما يمنحه نوعًا من البصمة.
استخدم الجدول التالي كمرجع:
| نوع الملف | الامتداد الشائع | نوع الوسائط | 'التوقيع' الثنائي |
|---|---|---|---|
| ELF | "exe" |
"application/octet-stream" |
0x7F, 0x45, 0x4C, 0x46 |
| BMP | "bmp" |
"image/bmp" |
0x42, 0x4D |
| PNG | "png" |
"image/png" |
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A |
| JPG | "jpg" |
"image/jpg" |
0xFF, 0xD8, 0xFF |
| GIF | "gif" |
"image/gif" |
0x47, 0x49, 0x46 |
نفّذ الدالة type_from_extension/1. ينبغي أن تأخذ امتداد ملف (سلسلة نصية) وتُرجع نوع الوسائط (سلسلة نصية)، أو nil إذا لم يطابق الامتداد الامتدادات المتوقعة.
FileSniffer.type_from_extension("exe")
# => "application/octet-stream"
FileSniffer.type_from_extension("txt")
# => nil
نفّذ الدالة type_from_binary/1. ينبغي أن تأخذ ملفًا (ثنائيًا) وتُرجع نوع الوسائط (سلسلة نصية)، أو nil إذا لم يطابق توقيع الملف التوقيعات المتوقعة.
file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"
file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil
لا تقلق بشأن قراءة الملف كبيانات ثنائية. افترض أن هذا قد تم نيابةً عنك، وأن الاختبارات تمرّره إليك كوسيط.
نفّذ الدالة verify/2. ينبغي أن تأخذ ملفًا (ثنائيًا) وامتدادًا (سلسلة نصية) وتُرجع صفًا :ok أو :error.
file = File.read!("application.exe")
FileSniffer.verify(file, "exe")
# => {:ok, "application/octet-stream"}
FileSniffer.verify(file, "png")
# => {:error, "Warning, file format and file extension do not match."}
سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.