c++ - std:: wstring VS std:: string




unicode c++-faq (8)

string ؟ wstring ؟

std::string is a basic_string templated on a char ، and std::wstring on a wchar_t .

char مقابل wchar_t

char المفترض أن يحمل الحرف حرفًا ، عادة ما يكون حرفًا واحدًا. wchar_t المفترض أن يحتفظ wchar_t بشخصية واسعة ، وبعد ذلك ، تصبح الأمور صعبة: في Linux ، يكون wchar_t 4 بايت ، بينما يكون في Windows 2 بايت

ماذا عن Unicode ، إذن؟

المشكلة هي أن لا char ولا wchar_t ترتبط مباشرة إلى unicode.

على لينكس؟

لنأخذ نظام تشغيل Linux: نظام Ubuntu الخاص بي مدرك بالفعل لـ unicode. عندما أعمل مع خيط char ، يتم ترميزه في UTF-8 (أي Unicode string of chars). التعليمة البرمجية التالية:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(bytes)     :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(bytes)    :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

يخرج النص التالي:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(bytes)     : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(bytes)    : 111 108 233

سترى بالفعل نص "olé" في char يتم إنشاؤه بالفعل بأربعة أحرف: 110 و 108 و 195 و 169 (بدون حساب الصفر الزائف). (سأسمح لك بدراسة كود wchar_t كممارسة)

لذا ، عند العمل باستخدام char على Linux ، يجب أن ينتهي بك الأمر عادة باستخدام Unicode دون حتى معرفته. و كما يعمل std :: string char ، لذلك std :: string بالفعل unicode-ready.

لاحظ أن سلسلة std :: ، مثل واجهة برمجة التطبيقات لسلسلة C ، ستعتبر سلسلة "olé" تحتوي على أربعة أحرف ، وليس ثلاثة أحرف. لذلك يجب عليك توخي الحذر عند اقتطاع / اللعب مع أحرف unicode لأن بعض تركيبة الأحرف الممنوعة في UTF-8.

على ويندوز؟

في Windows ، هذا مختلف قليلاً. كان Win32 لدعم الكثير من التطبيقات التي تعمل مع char وعلى charsets / codepages مختلفة المنتجة في جميع أنحاء العالم ، قبل ظهور يونيكود.

لذلك كان حلها مثيرًا للاهتمام: إذا كان التطبيق يعمل مع char ، فإن سلاسل char يتم ترميزها / طباعتها / عرضها على تسميات GUI باستخدام charset / codepage المحلي على الجهاز. على سبيل المثال ، سيكون "olé" هو "olé" في Windows الفرنسية المترجمة ، ولكن سيكون شيئًا مختلفًا على Windows السيريلية المترجمة ("olé" إذا كنت تستخدم Windows-1251 ). وبالتالي ، ستظل "التطبيقات التاريخية" تعمل عادة بنفس الطريقة القديمة.

بالنسبة إلى التطبيقات المستندة إلى Unicode ، يستخدم Windows wchar_t ، وهو بعرض 2 بايت ، ويتم ترميزه UTF-16 ، وهو Unicode مرمَّز على أحرف 2 بايت (أو على الأقل ، UCS-2 المتوافق في الغالب ، وهو تقريباً نفس الشيء IIRC).

ويقال أن التطبيقات التي تستخدم char هي "multibyte" (لأن كل حرف رسومي يتألف من char واحد أو أكثر من char ) ، بينما يتم استخدام التطبيقات التي تستخدم wchar_t "widechar" (لأن كل حرف g يتكون من wchar_t واحد أو اثنين. راجع MultiByteToWideChar و WideCharToMultiByte Win32 conversion API لمزيد من المعلومات.

وبالتالي ، إذا كنت تعمل على Windows ، فأنت تريد بشدة استخدام wchar_t (إلا إذا كنت تستخدم إطارًا يخفي ذلك ، مثل GTK+ أو QT ...). الحقيقة هي أنه خلف الكواليس ، يعمل Windows مع سلاسل wchar_t ، لذا فإن التطبيقات التاريخية حتى يتم تحويل سلاسل char الخاصة بها في wchar_t عند استخدام API مثل SetWindowText (دالة API ذات المستوى المنخفض لتعيين التسمية على واجهة مستخدم Win32 GUI).

مشاكل الذاكرة؟

UTF-32 عبارة عن 4 بايت لكل حرف ، لذا لا يوجد الكثير لإضافته ، فقط إذا كان نص UTF-8 ونص UTF-16 سيستخدم دائمًا كمية أقل أو نفس مقدار الذاكرة من نص UTF-32 (وعادة ما يكون أقل ).

إذا كان هناك مشكلة في الذاكرة ، فيجب أن تعرف أكثر من معظم اللغات الغربية ، فإن نص UTF-8 سيستخدم ذاكرة أقل من نفس UTF-16.

مع ذلك ، بالنسبة إلى اللغات الأخرى (الصينية ، اليابانية ، إلخ) ، ستكون الذاكرة المستخدمة هي نفسها ، أو أكبر بالنسبة إلى UTF-8 مقارنة بـ UTF-16.

وبشكل عام ، ستستخدم UTF-16 في الغالب 2 بايت لكل حرف (إلا إذا كنت تتعامل مع نوع معين من الحروف اللغوية الباطنية (Klingon؟ Elvish؟) ، بينما UTF-8 ستنفق من 1 إلى 4 بايت.

راجع http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 لمزيد من المعلومات.

استنتاج

1. متى يجب استخدام std :: wstring over std :: string؟

على لينكس؟ على الاغلب لا (§).
على ويندوز؟ تقريبا دائما (§).
على رمز عبر منصة؟ يعتمد على مجموعة الأدوات الخاصة بك ...

(§): ما لم تستخدم مجموعة أدوات / إطارًا تقول غير ذلك

2. هل يمكن أن تحتوي السلسلة std :: على كل مجموعة حروف ASCII بما في ذلك الحروف الخاصة؟

إشعار: سلسلة std :: مناسبة للاحتفاظ بمخزن مؤقت "ثنائي" ، حيث std :: wstring ليس!

على لينكس؟ نعم فعلا.
على ويندوز؟ فقط أحرف خاصة متوفرة للإعدادات المحلية الحالية لمستخدم Windows.

تحرير (بعد تعليق من Johann Gerell ): سلسلة std :: ستكون كافية للتعامل مع كل سلاسل الأحرف التي تستند إلى char (كل char يكون رقم من 0 إلى 255). لكن:

  1. من المفترض ASCII للانتقال من 0 إلى 127. الأحرف الأعلى ليست ASCII.
  2. سيعقد char من 0 إلى 127 بشكل صحيح
  3. سيكون للشارة من 128 إلى 255 إشارة اعتماداً على التشفير الخاص بك (unicode ، non-unicode ، إلخ) ، ولكنها ستكون قادرة على الاحتفاظ بكافة رموز الرسوم Unicode طالما تم ترميزها في UTF-8.

3. هل std :: wstring مدعوم من قبل جميع compilers C ++ شعبية تقريبا؟

في الغالب ، باستثناء دول مجلس التعاون الخليجي مقرها التي يتم نقلها إلى ويندوز
يعمل على بلدي ز + 4.3.2 (تحت Linux) ، واستخدمت Unicode API على Win32 منذ Visual C ++ 6.

4. ما هو بالضبط شخصية واسعة؟

في C / C ++ ، يكون نوع الحرف المكتوب wchar_t أكبر من نوع الحرف char البسيط. من المفترض أن يتم استخدامه لوضع أحرف داخلية تكون أرقامها (مثل رموز الحروف Unicode) أكبر من 255 (أو 127 ، بالاعتماد على ...)

لا أستطيع فهم الاختلافات بين std::string std::wstring و std::wstring . أعلم أن wstring يدعم wstring كبيرة مثل أحرف Unicode. لدي الأسئلة التالية:

  1. متى يجب استخدام std::wstring over std::string ؟
  2. هل يمكن أن تحتوي std::string على مجموعة حروف ASCII بأكملها ، بما في ذلك الحروف الخاصة؟
  3. هل std::wstring مدعوم من قبل جميع مترجمات C ++ الشائعة؟
  4. ما هو بالضبط " شخصية واسعة

  1. عندما تريد استخدام سلاسل Unicode وليس فقط ascii ، مفيدة للتدويل
  2. نعم ، لكنها لا تلعب بشكل جيد مع 0
  3. ليس على علم بأي شيء لا
  4. الحرف العريض هو طريقة محددة للملجم للتعامل مع تمثيل طول ثابت لحرف unicode ، ل MSVC هو حرف 2 بايت ، لدول مجلس التعاون الخليجي أفهم أنه 4 بايت. و +1 لـ http://www.joelonsoftware.com/articles/Unicode.html

  1. عندما تريد أن يكون لديك أحرف كبيرة مخزنة في السلسلة الخاصة بك. wide يعتمد على التنفيذ. افتراضيات Visual C ++ إلى 16 بت إذا كنت أتذكر بشكل صحيح ، بينما الافتراضيات GCC اعتماداً على الهدف. انها 32 بت طويلة هنا. يرجى ملاحظة wchar_t (نوع حرف واسع) لا علاقة لها unicode. إنه يضمن فقط أنه يمكن تخزين كافة أعضاء مجموعة الأحرف الأكبر التي يدعمها التطبيق بواسطة لغاته ، وعلى الأقل طالما char. يمكنك تخزين سلاسل unicode بشكل جيد في std::string باستخدام ترميز utf-8 أيضًا. لكنها لن تفهم معنى نقاط شفرة Unicode. لذا str.size() مقدار الأحرف المنطقية في السلسلة الخاصة بك ، ولكن مجرد مقدار عناصر char أو wchar_t المخزنة في هذه السلسلة / wstring. ولهذا السبب ، طور Glib::ustring + C ++ فئة الطبقة Glib::ustring التي يمكنها التعامل مع utf-8.

    إذا كان طول wchar_t 32 بت ، فيمكنك استخدام utf-32 كترميز unicode ، ويمكنك تخزين ومعالجة سلاسل unicode باستخدام ترميز ثابت (utf-32 ثابت الطول). هذا يعني أن الدالة s.size() في s.size() ستقوم بعد ذلك بإرجاع المقدار الصحيح من عناصر wchar_t والأحرف المنطقية.

  2. نعم ، دائمًا ما يكون char طوله 8 بتات تقريبًا ، مما يعني أنه يمكنه تخزين جميع قيم ASCII.
  3. نعم ، جميع المترجمين الرئيسيين يدعمونها.

1) كما ذكر Greg ، فإن wstring مفيد في التدويل ، وذلك عندما ستقوم بإصدار منتجك بلغات أخرى غير الإنجليزية

4) التحقق من ذلك للحصول على طابع واسع wchar_t


أوصي بتجنب std::wstring على Windows أو في أي مكان آخر ، إلا عند الحاجة من قبل الواجهة ، أو في أي مكان بالقرب من مكالمات Windows API وتحويلات الترميز الخاصة بها كسكر نحوي.

تم تلخيص وجهة نظري في http://utf8everywhere.org والتي أنا مؤلف مشارك لها.

ما لم يكن التطبيق الخاص بك هو API-call-centric ، على سبيل المثال تطبيق واجهة المستخدم بشكل أساسي ، فإن الاقتراح هو تخزين سلاسل Unicode في سلسلة std :: وترميزها في UTF-8 ، إجراء التحويل بالقرب من مكالمات API. الفوائد الواردة في المقال تفوق الانزعاج الظاهري للتحويل ، خاصة في التطبيقات المعقدة. هذا هو مضاعف لذلك لتطوير منصة متعددة والمكتبة.

والآن ، الإجابة على أسئلتك:

  1. بعض الاسباب الضعيفة. وهي موجودة لأسباب تاريخية ، حيث يعتقد أن widechars هي الطريقة الصحيحة لدعم Unicode. يتم استخدامه الآن لواجهة APIs التي تفضل سلاسل UTF-16. أنا استخدمها فقط في المنطقة المجاورة لمكالمات API.
  2. هذا ليس له علاقة std :: string. يمكن أن تحمل أي ترميز وضعت فيه. والسؤال الوحيد هو كيف تعامل المحتوى الخاص بك. توصيلي هو UTF-8 ، لذا سيكون بإمكانه الاحتفاظ بجميع أحرف unicode بشكل صحيح. إنها ممارسة شائعة في لينكس ، لكنني أعتقد أن برامج Windows يجب أن تقوم بها أيضًا.
  3. لا.
  4. حرف واسع هو اسم مربك. في الأيام الأولى من Unicode ، كان هناك اعتقاد بأن الحرف يمكن ترميزه في وحدتي بايت ، ومن هنا جاءت التسمية. واليوم ، يقف "لأي جزء من الحرف الذي يبلغ طوله وحدتي بايت". يُنظر إلى UTF-16 كتسلسل لأزواج البايت هذه (ويعرف أيضًا باسم الأحرف الكبيرة). تأخذ الأحرف في UTF-16 إما زوجًا واحدًا أو اثنين.

التطبيقات التي ليست راضية عن 256 حرفًا مختلفًا فقط لديها خيارات إما باستخدام أحرف عريضة (أكثر من 8 بتات) أو ترميز متغاير الطول (ترميز متعدد البتات في مصطلحات C ++) مثل UTF-8. تتطلب الأحرف العريضة عمومًا مساحة أكبر من ترميز بطول متغير ، ولكنها أسرع في المعالجة. عادةً ما تستخدم التطبيقات متعددة اللغات التي تعمل بكميات كبيرة من النص أحرفًا كبيرة عند معالجة النص ، ولكن يتم تحويله إلى UTF-8 عند تخزينه على القرص.

والفرق الوحيد بين string و wstring هو نوع بيانات الأحرف التي يتم تخزينها. يخزن سلسلة char s الذي يكون حجمه مضمونًا على الأقل 8 بت ، بحيث يمكنك استخدام سلاسل لمعالجة مثل ASCII أو ISO-8859-15 أو نص UTF-8. المعيار لا يقول شيئا عن مجموعة الأحرف أو الترميز.

عمليا ، يستخدم كل مترجم مجموعة أحرف تتوافق أول 128 حرفًا مع ASCII. هذا هو الحال أيضًا مع المترجمين الذين يستخدمون ترميز UTF-8. الشيء المهم الذي يجب أن تكون على دراية به عند استخدام السلاسل النصية في UTF-8 أو بعض ترميزات أخرى متغيرة الطول ، هي أن الفهارس والأطوال تقاس بالبايتات وليس بالأحرف.

نوع البيانات من wstring هو wchar_t ، الذي لم يتم تعريف حجمه في المعيار ، إلا أنه يجب أن يكون على الأقل بحجم char ، عادةً 16 بت أو 32 بت. يمكن استخدام wstring لتجهيز النص في ترميز الأحرف الكبيرة المحدد للتنفيذ. نظرًا لأن التشفير لم يتم تعريفه في المعيار ، فإنه ليس من السهل التحويل بين السلاسل والأوتار. لا يمكن للمرء أن يفترض أن يكون هناك ترميز ذو طول ثابت.

إذا لم تكن بحاجة إلى دعم متعدد اللغات ، فقد تكون بخير باستخدام السلاسل العادية فقط. من ناحية أخرى ، إذا كنت تكتب تطبيقًا رسوميًا ، فغالبًا ما تكون واجهة برمجة التطبيقات تدعم الأحرف العريضة فقط. ثم ربما تريد استخدام نفس الأحرف الواسعة عند معالجة النص. ضع في اعتبارك أن UTF-16 عبارة عن ترميز بطول متغير ، مما يعني أنه لا يمكنك افتراض length() لإرجاع عدد الأحرف. إذا كانت واجهة برمجة التطبيقات تستخدم ترميزًا بطول ثابت ، مثل UCS-2 ، فستصبح المعالجة سهلة. من الصعب إجراء التحويل بين الأحرف العريضة و UTF-8 بطريقة محمولة ، ولكن مرة أخرى ، قد تدعم واجهة برمجة التطبيقات لواجهة المستخدم التحويل.


لذا ، يجب أن يكون لدى كل قارئ هنا الآن فهم واضح للحقائق والوضع. إذا لم يكن كذلك ، فيجب عليك قراءة إجابة paercebal الشاملة بشكل رائع [راجع للشغل: شكرا!].

استنتاجي العملي بسيط للغاية: كل الأشياء التي ترمز إلى C ++ (و STL) "ترميز الشخصية" مكسورة بشكل كبير وغير مجدية. إلقاء اللوم على Microsoft أم لا ، لن يساعد ذلك على أي حال.

حلّي ، بعد تحقيق متعمق ، الكثير من الإحباط والتجارب المترتبة على ذلك هو ما يلي:

  1. القبول ، يجب أن تكون مسؤولاً عنك وحدك عن مواد الترميز والتحويل (وستلاحظ أن الكثير منها قليل التافهة)

  2. استخدام std :: string لأية سلاسل UTF-8 مشفرة (مجرد typedef std::string UTF8String )

  3. تقبل أن هذا الكائن UTF8String هو مجرد حاوية غبية ، ولكن رخيصة. لا تقم مطلقًا بالوصول إلى و / أو التلاعب بالأحرف الموجودة فيه مباشرة (بدون البحث والاستبدال وما إلى ذلك). هل يمكن ، ولكنك حقا حقا ، حقا لا أريد أن تضيع وقتك كتابة خوارزميات التلاعب النص لسلسلة متعددة البايت! حتى لو قام أشخاص آخرون بهذه الأشياء الغبية ، لا تفعل ذلك! فليكن! (حسنا ، هناك سيناريوهات حيث أنه من المنطقي ... مجرد استخدام مكتبة ICU لتلك).

  4. استخدم std :: wstring لـ UCS-2 ترميز السلاسل ( typedef std::wstring UCS2String ) - هذا حل وسط ، typedef std::wstring UCS2String WIN32 API). UCS-2 يكفي لمعظمنا (المزيد عن ذلك لاحقًا ...).

  5. استخدام مثيلات UCS2String عند الحاجة إلى وصول حرف - حرف - (قراءة ومعالجة وهكذا). يجب إجراء أي معالجة قائمة على الأحرف في تمثيل غير متعدد البايتات. انها بسيطة وسريعة وسهلة.

  6. إضافة وظيفتين مساعدتين لتحويل إلى الأمام والإياب بين UTF-8 و UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );
    

التحويلات واضحة ، يجب أن تساعد google هنا ...

هذا هو. استخدم UTF8String حيثما تكون الذاكرة ثمينة ولكل UTF-8 I / O. استخدم UCS2String في أي مكان يجب تحليل السلسلة و / أو معالجتها. يمكنك تحويل بين هذين التمثيل في أي وقت.

البدائل والتحسينات

  • يمكن تحقيق التحويلات من وإلى ترميزات الأحرف أحادية البايت (مثل ISO-8859-1) بمساعدة جداول الترجمة البسيطة ، على سبيل المثال const wchar_t tt_iso88951[256] = {0,1,2,...}; ورمز مناسب للتحويل من UCS2 وإليه.

  • إذا كانت UCS-2 غير كافية ، من التبديل إلى UCS-4 ( typedef std::basic_string<uint32_t> UCS2String )

ICU أو مكتبات unicode الأخرى؟

للأشياء المتقدمة.


متى يجب عدم استخدام أحرف عريضة؟

عندما تكتب رمزًا قبل عام 1990.

من الواضح أنني أتحدث ، لكن في الحقيقة ، إنه القرن الحادي والعشرون الآن. 127 حرفا منذ فترة طويلة توقفت لتكون كافية. نعم ، يمكنك استخدام UTF8 ، ولكن لماذا تهتم بالصداع؟





wstring