تحويل std::string إلى أحرف صغيرة: C++ تحت الغطاء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

سحر حالة الأحرف: لماذا أصبح سؤال بسيط كلاسيكيًا



تخيل: أنت تكتب برنامجًا بلغة C++ يجب أن يقارن أسماء المستخدمين. لكن "John" و"john" و"JOHN" هم نفس الشخص، أليس كذلك؟ أو أنك تقوم بتحليل ملف إعدادات حيث يمكن كتابة المفاتيح بأي حالة أحرف. وهنا تطرح سؤالًا يبدو بسيطًا: "كيفية تحويل std::string إلى أحرف صغيرة؟"



هذا السؤال على Stack Overflow جمع مئات الآلاف من المشاهدات وعشرات الإجابات. لماذا؟ لأن تحويل سلسلة إلى أحرف صغيرة في C++ ليس مجرد استدعاء دالة واحدة. إنه عالم كامل تلتقي فيه الإعدادات المحلية (locale) والترميزات والأداء والمكتبة القياسية. في هذه المقالة، سننظر تحت الغطاء ونفهم كيف تعمل هذه العملية بالفعل.



الإجابة الكلاسيكية: std::transform + std::tolower



إذا كنت قد بحثت عن "C++ string lower case example"، فمن المحتمل أنك رأيت مقطع الكود هذا:



#include <algorithm>#include <cctype>#include <string>

std::string to_lower_case(const std::string& str) { std::string result = str; std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); }); return result;}


هذا الكود يعمل، لكنه مجرد قمة جبل الجليد. دعنا نحلله بالتفصيل.



لماذا unsigned char وليس char؟



الخطأ الأكثر شيوعًا للمبتدئين هو تمرير قيمة من نوع char إلى std::tolower. الأمر أن std::tolower من <cctype> يقبل int ويتوقع أن تكون القيمة قابلة للتمثيل كـ unsigned char (أو تساوي EOF). إذا قمت بتمرير قيمة سالبة من char (وفي بعض التطبيقات يكون char موقعًا)، فسيكون السلوك غير محدد (undefined behavior).



لهذا نستخدم لامدا مع unsigned char:

[](unsigned char c) { return std::tolower(c); }


هذا يضمن أننا لا نتجاوز القيم المسموح بها.



البديل: حلقة for



بعض المطورين يفضلون نهجًا أكثر وضوحًا:



std::string to_lower_case_for(const std::string& str) {    std::string result;    result.reserve(str.size());    for (char ch : str) {        result.push_back(std::tolower(static_cast<unsigned char>(ch)));    }    return result;}


هذا الخيار أقل أناقة قليلاً، لكنه مكافئ تمامًا من حيث المعنى. يظهر بوضوح أننا نمر عبر كل حرف.



كيف يعمل تحت الغطاء: std::tolower



الدالة std::tolower ليست سحرًا. في معظم التطبيقات، تستخدم جدول بحث (lookup table). لكل قيمة محتملة من unsigned char (0-255)، يتم تخزين القيمة المقابلة بالأحرف الصغيرة مسبقًا في هذا الجدول. وبالتالي، استدعاء std::tolower('A') هو مجرد وصول سريع إلى عنصر مصفوفة عند الفهرس 65 (رمز الحرف 'A')، والذي يعيد 97 (رمز 'a').



لكن هناك فارق بسيط: الإعدادات المحلية (locale). افتراضيًا، يستخدم std::tolower الإعدادات المحلية "C"، التي تعمل فقط مع أحرف ASCII. إذا كنت تعمل مع أحرف روسية أو أحرف ألمانية (ä, ö, ü) أو تشكيلات فرنسية (é, è)، فإن std::tolower الكلاسيكي لن يساعدك - سيعيد الحرف الأصلي دون تغيير.



مشكلة الإعدادات المحلية: عندما لا تكفي دالة واحدة



للعمل مع الترميزات متعددة البايت والأبجديات الوطنية، يجب استخدام std::tolower مع معامل الإعدادات المحلية:



#include <locale>

std::string to_lower_cas

المدونات

توصيات الكتب