النصوص في C++: string وأهم دوالها
في درس المتغيرات تعرّفت على string سريعاً: نوع جاهز يريحك من مصفوفات الحروف المتعبة في C. لكن string يخفي عشرات الدوال المفيدة. في هذا الدرس ستتعلم كيف تقيس النص وتقطعه وتبحث فيه وتعدّله، وكيف تحوّله إلى أرقام والعكس.
كل أمثلة الدرس تحتاج هذين السطرين في أول الملف:
#include <iostream>#include <string>طول النص وحروفه
Section titled “طول النص وحروفه”النص سلسلة حروف، ولكل حرف رقم يبدأ من صفر، مثل المصفوفات تماماً:
string word = "Hello";cout << word.size() << endl;cout << word[0] << endl;cout << word.front() << " " << word.back() << endl;cout << boolalpha << word.empty() << endl;
for (char c : word) { cout << c << "-";}cout << endl;5HH ofalseH-e-l-l-o-size(): عدد الحروف، ولها اسم ثانٍ مطابق هوlength().word[0]: الحرف الأول، ونوعهchar.front()وback(): أول حرف وآخر حرف.empty(): هل النص فارغ؟ وboolalphaتجعلcoutتطبعtrueوfalseبدل 1 و 0.
تخيّل النص مثل عقد من الخرز: كل خرزة حرف، ولكل خرزة مكان مرقّم في الخيط.
النص قابل للتعديل
Section titled “النص قابل للتعديل”بعكس Java و C#، النص في C++ قابل للتعديل في مكانه:
string name = "sara";name[0] = 'S';name += " Ali";name.push_back('!');cout << name << endl;
name.pop_back();name.insert(0, "Dr. ");cout << name << endl;name.erase(0, 4);cout << name << endl;Sara Ali!Dr. Sara AliSara Ali+=: تضيف نصاً في النهاية، وpush_backتضيف حرفاً واحداً.insert(موضع, نص): تُدخل نصاً في موضع معيّن.erase(موضع, عدد): تحذف عدداً من الحروف ابتداءً من موضع.
التقطيع والبحث
Section titled “التقطيع والبحث”string email = "omar@example.com";size_t at = email.find('@');cout << at << endl;cout << email.substr(0, at) << endl;cout << email.substr(at + 1) << endl;
if (email.find('#') == string::npos) { cout << "لا توجد علامة #" << endl;}cout << boolalpha << email.starts_with("omar") << endl;cout << email.ends_with(".org") << endl;4omarexample.comلا توجد علامة #truefalsefind: تُرجع موضع أول ظهور. وإذا لم تجد شيئاً تُرجع القيمة الخاصةstring::npos.substr(بداية, طول): الرقم الثاني طول وليس موضع نهاية. وإذا حذفته أخذت حتى آخر النص.starts_withوends_with: متاحتان منذ C++20.
الاستبدال
Section titled “الاستبدال”replace تستبدل جزءاً محدداً بالموضع والطول. ومع find تستبدل كلمة بأخرى:
string text = "I like Java";size_t pos = text.find("Java");if (pos != string::npos) { text.replace(pos, 4, "C++");}cout << text << endl;I like C++المقارنة
Section titled “المقارنة”العلامة == تقارن محتوى النصين مباشرة، والعلامتان < و > تقارنان أبجدياً:
string a = "apple";string b = "banana";cout << boolalpha;cout << (a == "apple") << endl;cout << (a == b) << endl;cout << (a < b) << endl;cout << (string("Apple") == a) << endl;truefalsetruefalseالمقارنة حساسة لحالة الحروف: Apple لا تساوي apple. لتجاهل الحالة حوّل النصين إلى حروف صغيرة أولاً، كما سترى بعد قليل.
تحويل حالة الحروف
Section titled “تحويل حالة الحروف”لا توجد دالة جاهزة تحوّل النص كله، لكن الحلقة تؤدي المهمة مع toupper و tolower من المكتبة <cctype>:
#include <cctype>
string shout = "hello world";for (char& c : shout) { c = toupper(c);}cout << shout << endl;
int digits = 0;for (char c : string("Room 204B")) { if (isdigit(c)) digits++;}cout << digits << endl;HELLO WORLD3لاحظ char& في الحلقة الأولى: المرجع يجعلنا نعدّل الحرف الأصلي لا نسخة منه. والمكتبة نفسها فيها isdigit و isalpha و isspace لفحص نوع الحرف.
قراءة سطر كامل: getline
Section titled “قراءة سطر كامل: getline”cin >> name تتوقف عند أول مسافة، فلو كتب المستخدم «Omar Ali» ستقرأ «Omar» فقط. لقراءة السطر كله استخدم getline:
#include <iostream>#include <string>using namespace std;
int main() { string fullName; cout << "اكتب اسمك الكامل: "; getline(cin, fullName); cout << "مرحباً " << fullName << endl; cout << "عدد الحروف: " << fullName.size() << endl; return 0;}إذا كتب المستخدم Omar Ali سيظهر:
اكتب اسمك الكامل: Omar Aliمرحباً Omar Aliعدد الحروف: 8بين النصوص والأرقام
Section titled “بين النصوص والأرقام”string price = "250";int total = stoi(price) + 50;cout << total << endl;
double pi = stod("3.14");cout << pi * 2 << endl;
string label = "العدد: " + to_string(total);cout << label << endl;3006.28العدد: 300stoi: من نص إلىint. وstod: من نص إلىdouble.to_string: من رقم إلى نص. ضرورية لأن"نص" + 5لا تعمل كما تتوقع في C++.
تقسيم النص بـ stringstream
Section titled “تقسيم النص بـ stringstream”لا توجد دالة split جاهزة، لكن stringstream تعامل النص كأنه cin، فتسحب منه الكلمات واحدة واحدة:
#include <sstream>
stringstream words("red green blue");string word;int count = 0;while (words >> word) { count++; cout << count << ": " << word << endl;}
stringstream csv("10,20,30");string part;int sum = 0;while (getline(csv, part, ',')) { sum += stoi(part);}cout << sum << endl;1: red2: green3: blue60المعامل الثالث في getline هو الحرف الفاصل، وبه تقسّم أي نص مفصول بفواصل.
جرّب بنفسك
Section titled “جرّب بنفسك”- اقرأ جملة كاملة من المستخدم بـ
getline، واطبع عدد حروفها وعدد كلماتها باستخدامstringstream. - خزّن اسم ملف مثل
photo.png، واستخرج الامتداد باستخدامrfind('.')وsubstr. - اكتب دالة
string toLower(string s)تُرجع النص بحروف صغيرة. - تحدٍّ: اكتب دالة
bool isPalindrome(const string& s)تُرجعtrueإذا كانت الكلمة تُقرأ من الجهتين بنفس الشكل مثلlevel.
أخطاء شائعة
Section titled “أخطاء شائعة”- تجاهل npos: استخدام ناتج
findمباشرة فيsubstrدون فحصstring::nposيرمي استثناءout_of_rangeعندما لا يوجد النص. - دمج نصين حرفيين:
"Hello " + "World"خطأ ترجمة، لأن النصوص الحرفية ليستstring. اجعل أحدهماstring("Hello "). - موضع خارج الحدود:
word[10]في نص قصير سلوك غير معرَّف ولا يعطي رسالة خطأ. استخدمword.at(10)التي ترمي استثناءً واضحاً. - cin ثم getline: بعد
cin >> xيبقى رمز السطر الجديد في الانتظار، فتقرأgetlineسطراً فارغاً. أضفcin.ignore();بينهما.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما ناتج string("Hello").substr(1, 3)؟
الرقم الثاني في substr هو الطول: نبدأ من الموضع 1 ونأخذ ثلاثة حروف.
سؤال
ماذا تُرجع find إذا لم تجد النص المطلوب؟
تُرجع القيمة الخاصة string::npos، ويجب مقارنة الناتج بها قبل استخدامه.
الخلاصة
Section titled “الخلاصة”size()لعدد الحروف، و[i]وfront()وback()للوصول إليها.- النص في C++ قابل للتعديل:
+=وinsertوeraseوreplace. findتُرجع الموضع أوstring::npos، وsubstr(بداية, طول)تقطع.==و<تقارنان المحتوى، وgetlineتقرأ سطراً كاملاً بمسافاته.stoiوstodوto_stringللتحويل، وstringstreamلتقسيم النص.