المقالات العلمية

مقالات الشبكة العربية لمطوري الألعاب

ما هو الـ Unicode ؟

هذه المقالة محمية ضمن الحقوق الفكرية لشركة In|Framez Technology Corp.، ومرخصة للعرض فقط ضمن الشبكة العربية لمطوري الألعاب مع الموافقة الصريحة من المؤلف وشركة In|Framez Technology Corp.. لا يسمح بإعادة نشر هذه المقالة أو تعديلها دون الرجوع للمؤلف. يمنع النسخ والاقتباس دون ذكر المصدر والموافقة من المؤلف.

هل سألت نفسك هذا السؤال يوماً بينما كنت تعمل على المفكرة مثلاً في نظام تشغيل WinXP أو Win2000 و شاهدت عبارة (Encoding : Unicode) في حفظ الملف؟ نعم، إذاً سأحاول في هذه المقالة أن أشرح لك ما هو الـ Unicode و كيف تستفيد منه في برمجة برامجك.

النقاط التي سيتم تغطيتها اليوم

  • أصل الـ Unicode و فكرته.
  • كيفية جعل برنامجك يدعم لغات عالمية.

مستعد ، انطلق!

أصل الـ ASCII ...Unicode

لا يوجد مبرمج عل سطح هذه الأرض الجميلة لم يحتك بما يسمّى بالـ ASCII و الذي يعرّف بأنه عبارة عن مجموعة من القيم العددية التي يكافئ كل منها حرفاً في لوحة المفاتيح.

فمثلاً الحرف a يكافئ في جدول ASCII العدد 97. لاحظ أني قلت a و لم أقل A ذلك لأن لكل منهما رمز خاص به، فبينما كانت a تساوي 97 كانت A تساوي 65 . و من حسن الحظ أن هذه الأحرف متعاقبة، أعني أن لكل حرف قيمة ما، و قيمته مضافاً إليها 1 تعادل الحرف الذي يليه في الألفبائية، على عكس جداول الحروف التي سبقت الـ ASCII.


و يقسم الـ ASCII إلى أربعة أقسام التي هي:

  • 33 حرف تحكم (control keys).
  • 52 حرف هجائية (letters) و مسافة (space).
  • 32 رمز (symbols).
  • 10 أرقام (digits).

و مجموع هذه القيم 128. انتظر قليلاً، أنا لم أقل أن مجموع محارف (characters) الـ ASCII (في نظام ANSI/ISO 8859-1) يساوي 128. أجل حيث كان هناك إصدارين من ASCII : احتوى الأول على 128 محرف. و قد عمل بشكل جيد في أمريكا حيث لا لزوم للأحرف الصوتية في الإنكليزية (مثلاً كل من resume و résumé متعادلتان) و حيث لم يكن يوجد الرمز £ (British Pound)، ولكنه كان يفتعل الكثير من المشكلات في جنوبي و شمالي الولايات المتحدة حيث للأحرف الصوتية و رموزها أهمية كبيرة، و لو أنك نظرت لأبعد من ذلك ستجد لغات لا تعتمد أبداً على الألفبائية اللاتينية أمثال العربية و الكورية و اليابانية...


الإصدار الثاني من ASCII كان 256 حرف و يسمى هذا الإصدار ASCII (نظام ANSI/ISO 8859-1). و يعتبر إمتداداً للإصدار السابق حيث أضيف إلى 128 حرف السابقة 128 أخرى عبارة عن مجموعة من الرموز (مثل: رموز العملات) و الأحرف الصوتية. و الآن لا يزال جدول ASCII المظلوم (الذي يعبر عن كل محرف منه بـ 1 بايت فقط) غير كفؤ لكي يحتوي على جميع لغات العالم (بعضها يزيد عدد أحرفه عن 21،000 حرف!).

المحارف ذات العرض أو الحجم 2 بايت (Double-Byte character set) أو ما يسمى بـ (DBCS)

لحلِّ المشكلة السابقة علينا إيجاد جدول أوسع (أعرض) حتى يستوعب جميع حروف لغات العالم، وذلك بأن نستخدم قيمة بعرض 2 بايت للأحرف التي تحتاج ذلك و 1 بايت للأحرف السابقة و هكذا سنصبح قادرين على كتابة أحرف العالم الكثيرة مع الحفاظ على المساحة في الذاكرة (و التي كانت مهمة آنذاك). و قد اعتمدت Microsoft هذا النظام في إصدار نظم خاصة بالبلاد التي تحتاج إلى أحرف خاصة مثل: اليابان و الصين. ولكن ألا تلاحظ معي أن مشكلة سوف تظهر ألا و هي كيف سيعرف هذا المعالج أن هذا الحرف معرف بـ 1 بايت و ذاك بـ 2 بايت. ثم إذا كنت أستخدم مؤشراً لحرف من نوع char و يحتاج لـ 2 بايت ما هو البايت الذي يليه (طبعاً الحل في مثل هذه الحالات أن أجعل المؤشر يشير إلى موقع بداية مصفوفة ثم أتابع مع العنصر الذي يليه والذي يشكل البايت رقم 2).

المنقذ Unicode

المشكلة الأساسية في الحل السابق هو الفوضى العارمة التي سيحييها DBCS في معالجة ما إذا كان هناك بايت سابق و أين موقعه. و هنا يأتي الساحر Unicode بعد تطور المعاجات و الذواكر و يبتدع حلاً جديداً و هو جعل جميع الحروف (بما فيها ASCII و DBCS) ذات عرض 2 بايت!

ترا..ترا..ترا.. (هذا هو الحل العظيم الذي أمضينا صفحة كاملة و نحن نحاول أن نقدمه). و هذا سيجعل بمقدورنا التعبير عن 65،536 محرف، أي بأضعاف المحارف التي نحتاجها، فيدعم بذلك كل اللغات الموجودة في العالم. إن ما يميز الـ Unicode عن الـ DBCS هو وحدانية المعالجة حيث أن جميع الحرف متساوية في الحجم، فكلها بعرض 2 بايت.

هل هناك مساوئ للـ Unicode؟

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


ملاحظة: إذا كان عندك WinXP أو Win2000  يمكنك أن تحفظ ملف مفكرة على سبيل المثال مرة برموز الـ Unicode و أخرى برموز الـ ASCII و الذي سمّي في المفكرة برموز ANSI (لأنه مصمم وفق مقاييس ANSI كما أشرت سابقاً) و ستلاحظ أن حجم الملف في المرة الأولى هو ضعف حجم الملف في المرة الثانية.

إستخدام الـ Unicode في البرمجة

من المعروف أنه لتعريف متغير (variable) يحمل حرفاً من جدول ASCII نستخدم المعرّف char و بالتالي لتعريف مؤشر له نستخدم char*. و الآن ماذا نستخدم لتعريف حرف من جدول Unicode ؟ نستخدم wchar_t و الذي يعرف في الملف WCHAR.H على النحو:

typedef unsigned short wchar_t;

إن المتغير من نوع short هو 16-bit (في نظام Win32) و هو المطلوب. لذا لتعريف متغير يحمل حرفاً من جدول Unicode نستخدم ما يلي:

wchar_t c = 'a';
wchar_t c[] = L"Unicode";

لاحظ استخدامي للحرف L من (Literal) قبل إشارة التنصيص في المثال الثاني و التي تخبر أن كل محرف من مصفوفة المحارف التالية هو بعرض 2 بايت. و هناك طريقة ثانية لأداء نفس المهمة و هي باستخدام التعبير TEXT و المعرف على النحو:

#define TEXT(x) L##x

و يمكنك إستخدامه على النحو:

wchar_t c[] = TEXT("Unicode");

إجراءات Unicode

و الآن، وبعد أن عرفت كيف تعرف متغيراً من نوع wchar_t و تهيّؤه. يجب أن تعرف كيف تتعامل مع البيانات في هذا المتغير. فإذا أردت نسخه، معرفة عدد أحرفه أو مقارنته مع آخر فإنك تستخدم إجراءً ما في كل مرّة. لا شك أنك عملت مع المتغيرات من نوع char و طبقت عليها العمليات السابقة. ولكن هل علمت بوجود إجراءات خاصة بمتغيرات wchar_t. فمثلاً لو أردنا معرفة عدد محارف جملة (String) ما من نوع char فإننا نجد ذلك عن طريق استخدام الإجراء strlen(char*) على النحو التالي:

char c[] = "Hello";
printf ("The length of the variable (c) is %d", strlen(c));

و طبعاً ستكون النتيجة:

The length of the variable (c) is 5

و الآن إذا أردنا تطبيق هذا الإجراء على متغير من نوع wchar_t على النحو:

wchar_t w[] = TEXT("Hello");
printf ("The length of the variable (w) is %d", strlen((char*)w));    // Typecast to char*

ماذا ستكون النتيجة ؟ هل تستطيع أن تحزر ؟ خطأ! ليس 5 إنما على النحو:

The length of the variable (w) is 1

 

ولكن لماذا ؟ ما الذي حدث ؟ هذا بسيط. إذا كان عندنا متغير من النوع wchar_t ذو القيمة 'H أو (0x0048) فإنه يمثل بالرقم 48 ثم 00، فبالمثل يؤخذ ترحيبنا على الشكل:

0x0048 0x0065 0x006C 0x006C 0x006F

أو:

48 00 65 00 6C 00 6C 00 6F 00

و الآن راجع آلية عمل الإجراء strlen. حلل ثم أعطني الإجابة . . . . . هل سأنتظر طويلاً؟ حسناً، نعلم أن التابع strlen يعدّ كل حرف يمر عليه في مصفوفة الحروف حتى يصل إلى قيمة النهاية 0 (Terminator) فينهي العد و يقدم النتيجة. و الآن ماذا فعل إجراؤنا العزيز؟ عدّ أول حرف و هو الـ H ثم انتقل إلى الثاني فوجده 0 فأنهى العد و أعطانا النتيجة النهائية 1. و الآن ماذا؟ هل علينا إعادة صياغة كل إجراء حتى يعمل بشكل صحيح مع wchar_t؟ ليس تماماً، لأن C/C++ فعلت ذلك عنك و كل ما عليك هو معرفة هذا الإجراء و استخدامه بالشكل الصحيح. فمثلاً حتى تحصل على طول متغير من نوع wchar_t فما عليك إلا استخدام الإجراء wcslen(wchar_t *) على النحو:

wchar_t w = TEXT("Hello")
printf ("The length of the variable (w) is %d", wcslen(w));

الآن أنت محق لأن النتيجة هنا ستكون فعلاً:

The length of the variable (w) is 5

و بإمكانك استكشاف كل الإجراءات التي يمكن أن تحتاجها في العمل مع wchar_t في الملف WCHAR.H كما ستجد في هذا الملف الإجراءات التقليدية التي تستخدمها للتعامل مع المتغيرات من نوع char.

أحد أهم مشاكل الـ Unicode و حلها

و الآن هل يجب علي أن أكتب نسختين من برنامجي؟ واحدة بدون Unicode لتتوافق مع أنظمة التشغيل التي لا تدعمه و أخرى باستخدامه لكي أستطيع أن أجعل برنامجي متوافقاً مع عدة لغات تحت نظام تشغيل يدعم الـ Unicode ؟ بالطبع لا، فـ Microsoft فكرت بهذا الموضوع و وضعت الحل في الملف TCHAR.H فجعلت كل الإصدارات من الإجراءات و معرفات المتغيرات موحدة. كيف؟ بالإستناد إلى تعريفك (#define) لعبارة UNICODE أو عدمها في أول سطر من برنامجك، فإن المترجم ينتقي الإجراء المناسب. فمثلاً الإجراء يُـعرف _tcslen على أنه wcslen إذا كانت العبارة UNICODE معرفة في برنامجك، و على أنه strlen إذا لم تكن، وذلك بالشكل:

#ifdef UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif

و هلمّ جرّاً. حتى أن Micorsoft عرفت النوع TCHAR على أنه char أو wchar_t بالإستناد إلى نفس القاعدة.

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

أما الآن فأتمنى أن تكون قد استفدت من هذا المقال، و أتمنى أن تتاح لي الفرصة لكتابة مقالات أخرى.

لمزيد من المعلومات

  • Microsoft Visual Studio .NET Product Documentation: Unicode and Multibyte Character Set (MBCS) Support
  • من أجل مثال: Microsoft Visual Studio .NET Product Documentation: Unires Sample: Demonstrates the Use of Unicode Resource Files
  • Programming Windows Fifth Edition (Chalrez Petzold)
  • Unicode Standard Ver 2.0 (Addison Wesley)

المصطلحات المستخدمة

المصطلح معناه
Character محرف. قيمة عددية تمثل حرفاً من الأحرف الهجائية أو رقماً أو رمزاً خاصاً أو أي شيء آخر مما يستطيع المستخدم إدخاله عبر لوحة المفاتيح! إضافة إلى ذلك، قد يعبر المحرف عن أوامر تحكم غير مطبوعة (لا تظهر على الشاشة)، مثل المحرف NUL و (Backspace) BS و هكذا.
يعتمد نظام التشغيل على جدول ربط (Mapping Table) يربط القيم العددية للمحرف بمكافئاتها الرسومية، مثلاُ المحرف رقم 65 يرسم على الشكل A.
String مصفوفة حروف (جملة). و لمزيد من الدقة : مصفوفة محارف. إن الـ string هو أي سلسلة من المحارف المتعاقبة. مثلاً: “Hello” هي string كما أن “Hello, World!” أيضاً string.
ASCII إختصار للجملة: The American Standard Code for Information Interchange الشفرة الأمريكية الموحدة لتبادل المعلومات، و الصادرة عن هيئة ANSI. و هي عبارة عن جدول فيه مجموعة من الأرقام كل رقم يقابل محرفاً (حرفاً هجائياً، رمزاً، رقماً، أو زر تحكم) . و هي القيم التي يتعامل معها المعالج بدلاً من التعامل مع الرموز نفسها.
Unicode مثل ASCII، هو جدول يربط أغلب المحارف الموجودة في لغات العالم بقيم عددية من 2 بايت (16 بت)، مما يعطيه إمكانية التعبير عن 65,563 محرفاً. يدعم في جميع أنظمة التشغيل المعتمدة تقنية NT، مثل WinXP، Win2000، WinNT (بكافة إصداراته).
DBCS اختصار للجملة Double-Byte Character Set و هو (مثل Unicode)، جدول محارف عالمي.
اعتمد فيه على 2 بايت لبعض المحارف و بايت واحد فقط لبعضها الآخر (مما سبب بعض المشاكل) و السبب في هذا أن المساحة على زمن DBCS كانت مهمة جداً.
ANSI الأحرف الأولى من (American National Standards Institute)، المؤسسة الأمريكية للقياسيات الوطنية.
منظمة من مجموعات عمل صناعية غير مأجورة. تشكلت عام 1918م من أجل تطوير المواصفات القياسية لعمليات التجارة، التعامل و الاتصالات.
ANSI هي الممثل الأمريكي للمنظمة العالمية للمواصفات القياسية (ISO)، و قد طورت قواعد استخدام لغات البرمجة، متضمنةً C/C++، FORTRAN و COBOL.

أضف تعليقاً

Loading