لماذا لا يزال سؤال "UTF-8 byte[] to string" ملحًا؟
\n\nتخيل: لقد تلقيت مصفوفة بايتات من طلب شبكة، أو قرأت ملفًا ثنائيًا، أو ألغيت تسلسل بيانات من API. بداخلها — نص عادي بالروسية أو الإنجليزية أو رموز تعبيرية. لكن C# يرى فقط byte[]. مهمتك هي تحويل هذه الأصفار والواحدات إلى سلسلة string مفهومة.
قد يبدو الأمر بسيطًا؟ استدعاء طريقة واحدة — وينتهي الأمر. لكن على Stack Overflow، سؤال "How to convert UTF-8 byte[] to string" يجمع آلاف المشاهدات وعشرات الإجابات. لماذا؟ لأن الشيطان يكمن في التفاصيل: الترميز، الأداء، الأمان، والاستثناءات غير المتوقعة.
\n\nفي هذه المقالة، لن نقدم فقط كودًا جاهزًا. سنقوم بمقارنة المناهج، وقياس السرعة، ومناقشة المزالق، واختيار أفضل أداة لكل مهمة. إذا كنت تكتب بلغة C# وتعمل مع الشبكات أو الملفات أو التسلسل — فهذه المادة لك.
\n\nالمشكلة: byte[] ليس سلسلة
\n\nفي C#، يتم تخزين السلسلة string بترميز UTF-16. أما البايتات التي حصلت عليها، فمن المرجح أنها مشفرة بـ UTF-8 (معيار الإنترنت، JSON، معظم البروتوكولات النصية). التحويل المباشر غير ممكن — سيعطي المترجم خطأ.
نحتاج إلى وحدة فك ترميز تحول تسلسل بايتات UTF-8 إلى أحرف UTF-16. وهنا يبدأ الجزء المثير: لدى المطور ثلاثة مسارات مختلفة جوهريًا على الأقل.
\n\nالطريقة 1: الكلاسيكية — Encoding.UTF8.GetString
\n\nالطريقة الأكثر وضوحًا والتي توصي بها Microsoft هي استخدام الفئة الثابتة System.Text.Encoding:
byte[] utf8Bytes = { 72, 101, 108, 108, 111, 32, 208, 191, 209, 128, 208, 176, 208, 178, 208, 176 }; // "Hello права"\nstring result = Encoding.UTF8.GetString(utf8Bytes);\nConsole.WriteLine(result); // الإخراج: Hello права\n\nالإيجابيات:
\n- \n
- البساطة — سطر واحد من الكود. \n
- دعم جميع الترميزات، بما في ذلك UTF-8 وUTF-16 وASCII وLatin-1. \n
- معالجة مدمجة لـ BOM (علامة ترتيب البايت) — إذا كان هناك علامة UTF-8 في بداية المصفوفة، سيتم تجاهلها. \n
- الأمان — لا توجد كتل
unsafe. \n
السلبيات:
\n- \n
- كل استدعاء ينشئ سلسلة جديدة ومخزنًا مؤقتًا داخليًا. \n
- إذا كانت البايتات تالفة أو تحتوي على تسلسل UTF-8 غير صالح، فستلقي الطريقة افتراضيًا استثناء
DecoderFallbackException(إذا لم يتم ضبط fallback). \n - غير مثالية للمصفوفات الكبيرة جدًا (مئات الميغابايت) — نسخ مزدوج للبيانات. \n
متى تستخدمها؟
\nفي 95% من الحالات. هذا هو المعيار الصناعي. إذا كنت لا تكتب محللًا عالي التحميل أو لا تعمل مع غيغابايت من السجلات — استخدم Encoding.UTF8.GetString.
الطريقة 2: سريعة وخطيرة — unsafe مع المؤشرات
\n\nلأولئك الذين يطاردون السرعة ولا يخافون من unsafe. الفكرة: نحن نعلم أن UTF-8 وUTF-16 هما ترميزان مختلفان، ولكن إذا كانت المصفوفة تحتوي فقط على أحرف ASCII (رموز حتى 127)، فيمكن "تمديد" كل بايت إلى حرف ثنائي البايت دون فك ترميز.
unsafe string UnsafeAsciiBytesToString(byte[] bytes)\n{\n fixed (byte* pBytes = bytes)\n {\n return new string((sbyte*)pBytes, 0, bytes.Length);\n }\n}\n\nهذا الكود يعمل فقط مع ASCII! بالنسبة للنص الروسي (أحرف > 127)، ستكون النتيجة غير صالحة. لكن بالنسبة للإنجليزية الخالصة، فهو أسرع بمقدار 2-3 مرات من Encoding.UTF8.GetString.
الإيجابيات:
\n- \n
- أقل تكلفة إضافية — لا نسخ، لا فك ترميز. \n
- مثالي لتحليل رؤوس HTTP ومفاتيح JSON (إذا كانت ASCII). \n
السلبيات:
\n- \n
- خطير للأحرف متعددة البايت — تلف البيانات. \n
- يتطلب إذن
unsafeفي المشروع (غير مسموح به في كل مكان). \n - لا يعالج