تحديد مصير حزين من تنسيق الدالة printf أحرف Unicode في Visual C ++

ظهر دعم Unicode على نظام Windows في وقت سابق عن معظم أنظمة التشغيل الأخرى. لهذا السبب ، لم يتم حل العديد من المشكلات المرتبطة بتمثيل الأحرف في Windows بنفس الطريقة التي تم بها حل النظم الأخرى التي أرجأ مطوروها تطبيق المعيار الجديد إلى أوقات أفضل [1]. المثال الأكثر تحديدًا: في نظام Windows ، يتم استخدام ترميز UCS-2 لتمثيل أحرف Unicode. تم التوصية به من قبل Unicode Consortium لأن الإصدار 1.0 يدعم فقط 65،536 حرفًا [2]. بعد خمس سنوات ، غير الكونسورتيوم رأيه ، ولكن بعد ذلك فات الأوان لتغيير شيء ما في Windows ، حيث تم طرح Win32s و Windows NT 3.1 و Windows NT 3.5 و Windows NT 3.51 و Windows 95 بالفعل في السوق - استخدموا جميعهم تشفير UCS -2 [3].

لكن اليوم سنتحدث عن سلاسل التنسيق لوظيفة printf .

نظرًا لأن Unicode تم اعتماده على نظام Windows في وقت سابق من C ، فإن هذا يعني أن مطوري Microsoft اضطروا إلى معرفة كيفية تنفيذ الدعم لهذا المعيار في وقت التشغيل C. ونتيجة لذلك ، ظهرت ميزات مثل wcscmp و wcschr و wprintf . بالنسبة لتنسيق السلاسل في printf ، تم تقديم المؤهلات التالية لها:

  • يمثل ٪ s سلسلة من نفس عرض سلسلة التنسيق ؛
  • يمثل ٪ S سلسلة ذات عرض معكوس لعرض سلسلة التنسيق ؛
  • يمثل ٪ hs سلسلة عادية بغض النظر عن عرض سلسلة التنسيق ؛
  • يمثل٪ ws و ٪ ls سلسلة واسعة بغض النظر عن عرض سلسلة التنسيق.

كانت الفكرة هي كتابة كود مثل هذا:

TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %s.\n"), buffer); 

وعند التحويل البرمجي في وضع ANSI ، احصل على هذه النتيجة:

 char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %s.\n", buffer); 

وعند التحويل البرمجي في وضع Unicode - هذا [4]:

 wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer); 

نظرًا لأن محدد ٪ s يقبل سلسلة من نفس عرض سلسلة التنسيق ، سيعمل هذا الرمز بشكل صحيح في تنسيقي ANSI و Unicode. أيضًا ، يعمل هذا الحل على تبسيط عملية تحويل الشفرة المكتوبة بالفعل من تنسيق ANSI إلى تنسيق Unicode ، حيث يتم استبدال سلسلة العرض المطلوب بمصطلح ٪ s المحدد.

عندما تمت إضافة دعم Unicode رسميًا إلى C99 ، اعتمدت لجنة توحيد لغة C نموذجًا مختلفًا لسلسلة التنسيق لوظيفة printf :

  • يمثل٪ s و ٪ hs سلسلة منتظمة ؛
  • يمثل ٪ ls سلسلة واسعة.

هنا بدأت المشاكل. على مدار السنوات الست الماضية بحلول ذلك الوقت ، تم كتابة عدد كبير من البرامج التي تحتوي على حجم مليارات الدولارات لنظام Windows ، واستخدمت التنسيق القديم. كيف تكون مترجمي Visual C و C ++؟

تقرر البقاء على الطراز القديم وغير القياسي ، حتى لا يتم كسر جميع برامج Windows الموجودة في العالم.

إذا كنت تريد أن يعمل الرمز في بيئات وقت التشغيل التي تلتزم بالقواعد الكلاسيكية لـ printf وتلك التي تتبع قواعد المعيار C ، فسوف يتعين عليك تقييد نفسك على محددات ٪ hs للسلاسل العادية و ٪ ls للسلاسل الواسعة. في هذه الحالة ، يتم ضمان ثبات النتائج ، بغض النظر عما إذا كانت سلسلة التنسيق يتم تمريرها إلى وظيفة sprintf أو wsprintf .

 #ifdef UNICODE #define TSTRINGWIDTH TEXT("l") #else #define TSTRINGWIDTH TEXT("h") #endif TCHAR buffer[256]; GetSomeString(buffer, 256); _tprintf(TEXT("The string is %") TSTRINGWIDTH TEXT("s\n"), buffer); char buffer[256]; GetSomeStringA(buffer, 256); printf("The string is %hs\n", buffer); wchar_t buffer[256]; GetSomeStringW(buffer, 256); wprintf("The string is %ls\n", buffer); 

يسمح لك تعريف TSTRINGWIDTH منفصل بالكتابة ، على سبيل المثال ، هذا الكود:

 _tprintf(TEXT("The string is %10") TSTRINGWIDTH TEXT("s\n"), buffer); 

نظرًا لأن الأشخاص يحبون العرض التقديمي للمعلومات ، فإليك جدولاً يناسبك.


لقد سلطت الضوء على الأسطر ذات المؤهلات ، والتي تم تعريفها في C بنفس الطريقة كما في التنسيق الكلاسيكي المعتمد في Windows [5]. استخدم هذه التصفيات إذا كنت تريد أن ينتج الشفرة نفس النتائج في كلا التنسيقين.

الملاحظات

[1] يبدو أن إدخال Unicode في Windows قبل الأنظمة الأخرى كان ينبغي أن يمنح Microsoft ميزة الخطوة الأولى ، ولكن - على الأقل في حالة Unicode - تحولت إلى "لعنة الرائد" بالنسبة لهم ، لأن الباقي قرر الانتظار حتى أوقات أفضل ، عندما يكون هناك حلول أكثر واعدة (مثل تشفير UTF-8) ، وفقط بعد ذلك تقدم Unicode في أنظمتها.

[2] على ما يبدو ، فقد اعتقدوا أن 65،536 حرفًا كان يجب أن يكون كافيًا للجميع .

[3] تم استبداله لاحقًا بـ UTF-16. لحسن الحظ ، UTF-16 متوافق مع الإصدارات السابقة من UCS-2 لأحرف الشفرة التي يمكن تمثيلها في كلا الترميزين.

[4] بشكل رسمي ، يجب أن يبدو إصدار Unicode كما يلي:

 unsigned short buffer[256]; GetSomeStringW(buffer, 256); wprintf(L"The string is %s.\n", buffer); 

الحقيقة هي أن wchar_t لم يكن بعد نوعًا مستقلاً ، وحتى يتم إضافته إلى المعيار ، كان مجرد مرادف للاختصار غير الموقَّع . يمكن العثور على التقلبات والمنعطفات في المصير wchar_t في مقالة منفصلة .

[5] ظهر التنسيق الكلاسيكي الذي طورته شركة Windows أولاً ، لذا كان من المحتمل أن يتكيف المعيار C معه ، وليس العكس.

ملاحظة المترجم

أنا ممتن للمؤلف لهذا المنشور. أصبح من الواضح الآن كيف اتضح كل هذا الالتباس مع "٪ s". والحقيقة هي أن المستخدمين طرحوا باستمرار السؤال لماذا يتفاعل PVS-Studio بشكل مختلف مع الكود "المحمول" ، كما يبدو لهم ، اعتمادًا على ما إذا كانوا يجمعون مشروعهم في نظام Linux أو Windows. كان من الضروري إنشاء قسم منفصل خاص في وصف تشخيصات V576 المكرسة لهذا الموضوع (راجع "الخطوط العريضة"). بعد هذا المقال ، يصبح كل شيء أكثر وضوحًا ووضوحًا. أعتقد أنه يجب قراءة هذه الملاحظة لكل من يقوم بتطوير التطبيقات عبر الأنظمة الأساسية. اقرأ وأخبر الزملاء.

Source: https://habr.com/ru/post/ar466875/


All Articles