معالجة السلاسل النصية في لغة C++

تدعم لغة البرمجة C ++ معالجة السلاسل النصية ، ويتم ذلك في الغالب من خلال مكتبتها القياسية . يحدد معيار اللغة عدة أنواع من السلاسل النصية، بعضها موروث من لغة C ، وبعضها الآخر مصمم للاستفادة من ميزات اللغة، مثل الفئات و RAII . النوع الأكثر استخدامًا هو `string` std::string، بينما std::string_viewيُستخدم `string` لعرض السلاسل النصية غير المالكة.

بما أن الإصدارات الأولية من لغة C++ كانت تحتوي فقط على وظائف واتفاقيات معالجة السلاسل النصية "منخفضة المستوى" الخاصة بلغة Cstd::string ، فقد تم تصميم العديد من التصميمات غير المتوافقة لفئات معالجة السلاسل النصية على مر السنين ولا تزال تستخدم بدلاً من ذلك ، وقد يحتاج مبرمجو C++ إلى التعامل مع اتفاقيات متعددة في تطبيق واحد.

تاريخ

يُعدّ هذا std::stringالنوع نوع البيانات الرئيسي للسلاسل النصية في لغة C++ القياسية منذ عام 1998، ولكنه لم يكن دائمًا جزءًا منها. فقد ورثت C++ من لغة C اصطلاح استخدام السلاسل النصية المنتهية بـ NUL ، والتي تُعالج بواسطة مؤشر إلى عنصرها الأول، بالإضافة إلى مكتبة من الدوال التي تُعالج هذه السلاسل. في لغة C++ القياسية الحديثة، لا يزال النص الحرفي مثل `string` "hello"يُشير إلى مصفوفة من الأحرف منتهية بـ NUL. [ 1 ]

يُوفر استخدام فئات C++ لتنفيذ نوع سلسلة نصية العديد من المزايا، منها إدارة الذاكرة المؤتمتة وتقليل مخاطر الوصول خارج النطاق، [ 2 ] بالإضافة إلى بنية أكثر سهولة لمقارنة السلاسل النصية ودمجها. لذلك، كان من المغري جدًا إنشاء فئة كهذه. على مر السنين، أنتج مطورو تطبيقات ومكتبات وأطر عمل C++ تمثيلات خاصة بهم للسلاسل النصية غير متوافقة، مثل تلك الموجودة في مكتبة المكونات القياسية لشركة AT&T (أول تطبيق من هذا النوع، 1983) [ 3 ] أو النوع الموجود في MFCCString من مايكروسوفت [ 4 ] أو النوع الموجود في Qt . [ 5 ] [ 6 ]QString

تختلف أنواع السلاسل النصية لدى مختلف الموردين في استراتيجيات التنفيذ وخصائص الأداء، مما جعل تحويل التعليمات البرمجية وحتى عملية التعيين من نوع إلى آخر أمرًا صعبًا:

  • استخدم البعض استراتيجية النسخ عند الكتابة مع آثار كبيرة على الأداء (مما جعل بعض العمليات أسرع بكثير وبعضها أبطأ بكثير).
  • على الرغم من أن معظمهم اتفقوا على صيغة المقارنة والتخصيص والدمج؛ إلا أن صيغة استخراج السلاسل الفرعية أو إجراء عمليات البحث اختلفت على نطاق واسع.
  • لم يكن كل شيء متوافقًا مع نظام يونيكود، مما أدى إلى اختلافات واسعة النطاق في كيفية تخزين الترميز، أو حتى في تخزينه من الأساس، وفي وقت إجراء الترجمة، وأحيانًا إلى استخدام عوامل إسناد مكلفة للغاية. كما كان يُعتقد أن وحدات الترميز الأكبر من البايتات ضرورية لدعم يونيكود، مما أدى إلى أنواع سلاسل نصية لا يمكنها التفاعل فيما بينها بكفاءة. [ 7 ]

على الرغم من وجود std::stringسلاسل نصية قياسية، لا تزال التطبيقات القديمة تحتوي عادةً على أنواع سلاسل نصية مخصصة، وقد تتوقع المكتبات سلاسل نصية على نمط لغة C، مما يجعل تجنب استخدام أنواع سلاسل نصية متعددة في برامج C++ "شبه مستحيل" [ 1 ] ، ويُلزم المبرمجين بتحديد تمثيل السلسلة النصية المطلوب قبل بدء المشروع. [ 4 ] في استعراض لتاريخ لغة C++ عام 1991، وصف مخترعها بيارن ستروستروب غياب نوع سلسلة نصية قياسي (وبعض الأنواع القياسية الأخرى) في C++ 1.0 بأنه أسوأ خطأ ارتكبه في تطويرها؛ إذ قال: "أدى غيابها إلى إعادة اختراع العجلة من قِبل الجميع، وإلى تنوع غير ضروري في الفئات الأساسية". [ 3 ]

وصف

يُعدّ هذا std::stringالصنف التمثيل القياسي لسلسلة نصية منذ C++98 . يوفر هذا الصنف بعض عمليات السلاسل النصية الشائعة مثل المقارنة، والدمج، والبحث والاستبدال، ودالة لاستخراج السلاسل الفرعية . std::stringيمكن إنشاء سلسلة نصية من سلسلة نصية بنمط C، كما يمكن استخراج سلسلة نصية بنمط C من سلسلة نصية بنمط C. [ 8 ] الوحدات الفردية التي تُكوّن السلسلة النصية هي من نوع char . في الاستخدام الحديث، غالبًا ما لا تُعتبر هذه الوحدات "أحرفًا" منفردة، بل أجزاء من ترميز أحرف متعدد البايتات مثل UTF-8 .

استيراد std ؛باستخدام std :: string ;int main () { string foo = "fighters" ; string bar = "stool" ; if ( foo != bar ) { std :: println ( "The strings are different!" ); } foo += bar + " end" ; // foo is now "fightersstool end" std :: println ( "{}" , foo ); // print }

يمكن إنشاء سلسلة نصية من سلسلة نصية حرفية باستخدام المعامل : على سبيل المثال، [ 9 ]std::literals::string_literals::operator""sstrings="Hello, world!"s;

نسخة إلى كاتب

يعني تطبيق النسخ عند الكتابة

string a = "hello!" ; string b = a ; // مُنشئ النسخ

لا يتم نسخ محتوى السلسلة النصية فعليًا aإلى السلسلة النصية الأخرى b؛ بل تتشارك السلسلتان النصيتان محتواهما، ويتم زيادة عداد مرجعي للمحتوى. ويؤجل النسخ الفعلي حتى تحدث عملية تغيير، مثل تغيير حرف، تجعل محتوى السلسلتين النصيتين مختلفًا.

سمحت معايير لغة C++ الأولية عمدًا باستراتيجية النسخ عند الكتابة، std::stringنظرًا لاعتبارها تحسينًا مفيدًا، وقد استخدمتها جميع التطبيقات تقريبًا. [ 8 ] مع ذلك، وُجدت أخطاء، لا سيما أن الدالة operator[]أعادت مرجعًا غير ثابت لتسهيل نقل كود C s[5] = 'a'، مما استدعى عملية نسخ. تسبب تعدد الخيوط في فشل التحسينات (مثل عدم النسخ عندما يكون عدد المراجع واحدًا) [ 10 ] ، وفي المعالجات الحديثة، يُعد التأمين اللازم لفحص عدد المراجع أو تغييره أكثر تكلفة من نسخ سلسلة نصية صغيرة. [ 11 ]

أدى ذلك إلى ابتعاد تطبيقات البرمجة، بدءًا من MSVC ثم GCC، عن تقنية النسخ عند الكتابة. [ 12 ] وقد تم حظر هذا التحسين نهائيًا في C++11 . [ 13 ]

تعتمد معظم التطبيقات حاليًا على تحسين السلاسل النصية الصغيرة (SSO)، حيث تُخزَّن السلاسل النصية التي يقل طولها عن 16 (أو 23) بايت في كائن السلسلة النصية دون الحاجة إلى تخصيص مخزن مؤقت. [ 14 ] يُعدّ هذا أسرع بكثير من عدّ المراجع، نظرًا لأن نسبة كبيرة من السلاسل النصية تكون بهذا الحجم الصغير، كما أن تخصيص المخزن المؤقت مكلف للغاية مقارنةً بنسخ كتل الذاكرة المحلية.

عرض السلسلة

على عكس لغات أخرى، مثل جافاjava.lang.String أو سي شاربSystem.String ، فإن سلاسل C++ قابلة للتغيير دائمًا، حيث أن ثوابت السلاسل النصية المُقتبسة والمنتهية بـ null تُغني عن معظم وظائف الفئة غير القابلة للتغيير. تتطلب لغة C++ أن يقوم الكود التالي بنسخ السلسلة النصية، وهذا بطيء نسبيًا مقارنةً بتمرير مؤشر، حتى مع استخدام عدادات المراجع للنسخ عند الكتابة. [ 11 ]

استيراد std ؛باستخدام std :: string ;void outputString ( string s ) { std :: print ( "{}" , s ); }// ... string s = "..." ; outputString ( s ); // ينشئ نسخة من s outputString ( "this is a literal string" ); // ينسخ السلسلة، أحيانًا مرتين

على الرغم من أن المترجم يمكنه تحسين هذا الأمر للوظائف المضمنة، إلا أن القليل اعتمد على ذلك، ودائمًا ما يتم تمرير السلاسل كمرجع ثابت : [ 15 ] كما أن التحويل من ثابت سلسلة يتطلب إنشاء متغير مؤقت std::stringوكان بطيئًا، مما يؤدي عادةً إلى وظائف محملة بشكل زائد:

استيراد std ؛باستخدام std :: string ;void outputString ( const string & s ) { std :: print ( "{}" , s ); }void outputString ( const char * s ) { std :: print ( "{}" , s ); }// ... string s = "..." ; outputString ( s ); // لا تنسخ s، بل تمرر مؤشرًا/مرجعًا outputString ( "this is a literal string" ); // تستدعي الدالة المُحمّلة بشكل زائد وتمرر مؤشرًا خامًا

يضيف معيار C ++17std::string_view الفئة [ 16 ] التي هي مجرد مؤشر وطول للبيانات للقراءة فقط، وهي بديل مباشر للمتغير المؤقتstd::string غير القابل للتغيير وتجعل تمرير الوسائط بالقيمة أسرع من أي من المثالين أعلاه:

استيراد std ؛باستخدام std :: string ؛ باستخدام std :: string_view ؛void outputString ( string_view sv ) { std :: print ( "{}" , s ); }// ... string s = "..." ; outputString ( s ); // مستوى واحد أقل من التوجيه غير المباشر outputString ( "this is a literal string" ); // علاوة على ذلك، قد يقوم المترجم بتحسين الكود بحيث لا نحتاج إلى استخدام strlen()

يجب إعادة كتابة جميع الدوال في المكتبة التي تأخذ وسيطًا من نوع سلسلة نصية للاستفادة من هذا في C++17، حيث أن التحويل من std::string_viewإلى std::stringلا يزال مكلفًا.

يمكن إنشاء عرض سلسلة نصية من سلسلة نصية حرفية باستخدام المعامل: على سبيل المثال، [ 17 ]std::literals::string_view_literals::operator""svstring_viewsv="Hello, world!"sv;

وحدات ترميز أخرى

std::stringهو تعريف نوعي لنسخة معينة من std::basic_string<CharT, Traits, Alloc>فئة القالب . [ 18 ] يوجد تعريفه في ملف الرأس <string> :

namespace std { using string = basic_string < char > ; }

يوجد صنف مشابه std::wstring، يتكون من wchar_t ، ويُستخدم غالبًا لتخزين نصوص UTF-16 على نظام ويندوز و UTF-32 على معظم الأنظمة الشبيهة بنظام يونكس . مع ذلك، لا يفرض معيار C++ أي تفسير على هذه الأنواع كنقاط ترميز يونيكود أو وحدات ترميز، ولا يضمن حتى أن يحتوي على عدد بتات أكبر من . [ 19 ] لحل بعض حالات عدم التوافق الناتجة عن خصائص ، أضاف C++11 صنفين جديدين: و (المكونين من النوعين الجديدين و )، وهما يمثلان عدد البتات المحدد لكل وحدة ترميز على جميع الأنظمة. [ 20 ] كما أضاف C++11 أيضًا سلاسل نصية جديدة من "أحرف" 16 بت و32 بت، بالإضافة إلى صيغة لوضع نقاط ترميز يونيكود في سلاسل نصية منتهية بـ null (على غرار لغة C). [ 21 ]wchar_tcharwchar_tstd::u16stringstd::u32stringchar16_tchar32_t

std::basic_string<CharT, Traits, Alloc>يضمن أن يكون A قابلاً للتخصيص لأي نوع std::char_traits<CharT>مصحوب ببنية. اعتبارًا من C++11، يلزم تنفيذ تخصيصات و و charفقط wchar_t. [ 22 ] في ، يكون لمعاملي القالب و القيم الافتراضية و افتراضيًا.char16_tchar32_tstd::basic_string<CharT, Traits, Alloc>TraitsAllocstd::char_traits<CharT>std::allocator<CharT>

A std::basic_stringهي أيضًا حاوية مكتبة قياسية ، وبالتالي يمكن تطبيق خوارزميات المكتبة القياسية على وحدات التعليمات البرمجية في السلاسل النصية.

الانتقادات

وقد اعتبر هيرب ساتر تصميم هذا البرنامج std::stringمثالاً على التصميم المتجانس ، حيث يرى أنه من بين 103 دالة عضو في الفئة في لغة C++98، كان من الممكن فصل 71 دالة دون فقدان كفاءة التنفيذ. [ 23 ]

انظر أيضاً

مراجع

  1. 1 2 سيكورد، روبرت سي. (2013). البرمجة الآمنة في لغتي C و C++ . أديسون-ويسلي. ISBN 9780132981972.
  2. أوالين، ستيف (2003). البرمجة العملية بلغة C++ . أورايلي.
  3. 1 2 ستروستروب، بيارن (1993). تاريخ لغة C++: 1979-1991 (ملف PDF) . وقائع مؤتمر ACM لتاريخ لغات البرمجة.
  4. 1 2 سولتر، نيكولاس أ.؛ كليبر، سكوت ج. (2005). لغة سي++ الاحترافية . جون وايلي وأولاده. ص 23. ISBN  9780764589492.
  5. فئة QString
  6. بلانشيت، ياسمين؛ سمرفيلد، مارك (2008). برمجة واجهات المستخدم الرسومية بلغة C++ باستخدام Qt4 . بيرسون للتعليم. ISBN 9780132703000.
  7. "فئة wstring_convert" . docs.microsoft.com . 3 أغسطس 2021. تم الاطلاع عليه في 26 ديسمبر 2021 .
  8. 1 2 مايرز، سكوت (2012)، STL الفعال ، أديسون-ويسلي، ص 64-65 ، ISBN  9780132979184
  9. cppreference.com. "std::literals::string_literals::operator""s" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 19 يونيو 2026 .
  10. "21334 - عدم وجود أمان متزامن متوافق مع معيار Posix في STD::basic_string" .
  11. 1 2 ساتر، هيرب (1999). "التحسينات التي ليست كذلك (في عالم متعدد الخيوط)" . مجلة مستخدمي C/C++ . 17 (6).
  12. سيلف بوت (17 يناير 2024). "تعديل غير متوقع لسلسلة نصية في لغة C++ ناتج عن تقنية النسخ عند الكتابة (COW)" . selfboot.cn . تم الاطلاع عليه بتاريخ 13 مايو 2025 .
  13. ميريديث، أليستير؛ بوهم، هانز؛ كراول، لورانس؛ ديموف، بيتر (2008). "تعديلات التزامن على السلاسل الأساسية" . ISO/IEC JTC 1/SC 22 /WG 21. تم الاطلاع عليه بتاريخ 19 نوفمبر 2015 .
  14. "داخل STL: الوتر - الشيء الجديد القديم" . تم الاطلاع عليه بتاريخ 17 يونيو 2026 .
  15. ستروستروب، بيارن؛ ساتر، هيرب، محرران. (8 مايو 2025). "إرشادات C++ الأساسية" . إرشادات C++ الأساسية . تم الاطلاع عليه في 13 مايو 2025 .
  16. "std::basic_string_view – cppreference.com" . en.cppreference.com . تم الاطلاع عليه بتاريخ 23 يونيو 2016 .
  17. cppreference.com. "std::literals::string_view_literals::operator""sv" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 19 يونيو 2026 .
  18. "مرجع C++ للسلسلة الأساسية" . Cppreference.com . تم الاطلاع عليه بتاريخ 11 يناير 2011 .
  19. جيلام، ريتشارد (2003). تبسيط يونيكود: دليل عملي للمبرمجين حول معيار الترميز . أديسون-ويسلي بروفيشنال. ص 714. ISBN  9780201700527.
  20. "ورقة C++11 رقم N3336" . المعايير المفتوحة . لغة البرمجة C++، فريق عمل المكتبة. 13 يناير 2012. تم الاطلاع عليه في 2 نوفمبر 2013 .
  21. ستروستروب، بيارن (2013). لغة البرمجة سي++ . أديسون ويسلي. ص 179. مؤرشف من الأصل في 25 نوفمبر 2015. تم الاطلاع عليه في 24 نوفمبر 2015 . 
  22. "char_traits – C++ Reference" . تم الاطلاع عليه في 1 أغسطس 2015 .
  23. ساتر، أعشاب. "الأحجار الضخمة "غير المربوطة"" . gotw.ca . تم الاطلاع عليه بتاريخ 23 نوفمبر 2015 .