Menu
flag Ar iconالعربيةdown icon

تدفقات السلاسل النصية في C++: التحليل والبناء باستخدام stringstream

كيفية استخدام std::stringstream و istringstream و ostringstream لتحليل النص، وتقسيم السلاسل النصية حسب المسافات، والتحويل بين السلاسل النصية والأرقام، وبناء سلاسل نصية منسّقة في الذاكرة.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

تدفق يعيش داخل سلسلة نصية

أنت تعرف بالفعل cin و cout - تدفقان موصولان بلوحة المفاتيح والشاشة. تدفق السلسلة النصية هو الفكرة نفسها، إلا أن البيانات تعيش بدلاً من ذلك في std::string داخل الذاكرة. تكتب إليه بـ << وتسحب منه القيم بـ >>، تماماً مثل تدفقات الطرفية، لكن لا شيء يمسّ الطرفية.

تحل هذه الفكرة الواحدة مشكلتين يوميتين: تحليل النص وتفكيكه إلى قيم ذات أنواع، وبناء سلسلة نصية منسّقة من أجزاء مختلطة. كل شيء يعيش في الترويسة <sstream> ويأتي في ثلاثة أنواع:

  • istringstream - للقراءة فقط، لتحليل المدخلات.
  • ostringstream - للكتابة فقط، لبناء المخرجات.
  • stringstream - في كلا الاتجاهين.

يعيد ss.str() كل ما كُتب حتى الآن كـ string عادية. أجرى مشغّلو << التنسيق نفسه من int إلى نص الذي يفعله cout - أنت فقط التقطت النتيجة بدلاً من طباعتها.

التحليل: استخراج قيم ذات أنواع

تظهر القوة الحقيقية مع >>. أعطِ تدفقاً بعض النص، ويقوم الاستخراج بتحويل كل رمز إلى النوع الذي تطلبه، متجاوزاً المسافات بينها. هذه هي الطريقة النظيفة لتقسيم سطر واحد إلى عدة حقول ذات أنواع:

يقرأ كل >> حتى المسافة التالية ويحوّل: "Ada" إلى string، و"36" إلى int، و"5.5" إلى double. لاحظ istringstream هنا - النوع المخصص للإدخال فقط يوضّح أنك تقوم بالتحليل لا غير.

تحويل السلاسل النصية إلى أرقام (بأمان)

من المهام المتكررة تحويل سلسلة نصية واحدة مثل "42" إلى int. يفعل stringstream ذلك، ويخبرك أيضاً بما إذا كان النص رقماً صالحاً فعلاً - وهو ما لا يفعله atoi أبداً:

هنا يقرأ ss >> value القيمة 123، ويتوقف عند a، وينجح - لذا تحقّق دائماً من السلسلة النصية بأكملها إن كان ذلك مهماً. يتمثل الفحص المتين في قراءة الرقم ثم التأكد من عدم بقاء أي شيء ذي معنى. للتحويلات البسيطة ذات القيمة الواحدة، فإن std::stoi و std::stod وأمثالها (مشروحة مع تحويل الأنواع) أقصر؛ لجأ إلى stringstream عندما تحمل سلسلة نصية واحدة عدة قيم مختلطة.

تقسيم سلسلة نصية حسب فاصل

يُعد تقسيم النص أكثر مهام تدفق السلاسل النصية بحثاً على Google. اجمع بين تدفق وstd::getline، الذي يمكنه القراءة حتى أي محرف فاصل، لا سطر جديد فحسب:

يكرّر نمط while (getline(...)) حتى ينفد التدفق، لأن getline يعيد التدفق، ويُقيَّم التدفق على أنه false بمجرد ألا يبقى شيء. احذف الوسيط الثالث فتقسم بدلاً من ذلك حسب الرموز المفصولة بمسافات باستخدام >> - اختر الفاصل الذي تستخدمه بياناتك.

بناء السلاسل النصية في الذاكرة

في الاتجاه الآخر، يجمّع ostringstream سلسلة نصية منسّقة دون خطر تجاوز المخزن المؤقت الذي في sprintf ولا الدمج اليدوي الهش. وهو مثالي لأسطر السجلات أو أسماء الملفات أو الرسائل المؤلّفة من أرقام ونص:

كل تنسيق <iomanip> الذي قد تستخدمه مع cout - setw و setfill و setprecision و hex - يعمل بالطريقة نفسها على تدفق السلسلة النصية، فتحصل على مخرجات محشوّة أو ذات عرض ثابت أو ست عشرية مُلتقطة مباشرة في string.

المطبّ: أعد الضبط قبل إعادة الاستخدام

الفخ الذي يقع فيه الجميع هو إعادة استخدام تدفق دون إعادة ضبطه. بمجرد أن يبلغ التدفق نهاية بياناته، تنغلق رايتاه eof و fail، وكل >> لاحق لا يفعل شيئاً بصمت - لا خطأ، بل قيم قديمة فحسب:

stringstream ss("10");
int a, b;
ss >> a;            // a = 10، التدفق الآن عند النهاية -> رُفعت راية eof
ss.str("20");       // حمّل نصاً جديداً...
ss >> b;            // يفشل بصمت - رايات الخطأ لا تزال مرفوعة، و b دون تغيير

يجب أن تمسح حالة الخطأ و المحتوى معاً. الترتيب الصحيح هو clear() أولاً، ثم str():

خطآن متصلان: يعيد ss.clear() ضبط الرايات لكنه لا يفرّغ المخزن المؤقت (استخدم ss.str("") لذلك)، وstringstream الذي تواصل الإضافة إليه بـ << سيستمر في النمو - أنشئ واحداً جديداً داخل الحلقة بدلاً من إعادة استخدام واحد متّسخ في كل تكرار.

التالي: المصفوفات

أعطتك تدفقات السلاسل النصية وسيلة نظيفة لتحويل النص إلى قيم ذات أنواع والعودة مجدداً - غالباً قائمة كاملة منها، مثل الـ vector الذي ملأته أثناء تقسيم CSV. لتخزين وفهرسة مجموعات ثابتة من تلك القيم، تحتاج إلى أبسط حاوية في اللغة. سنتناول بعد ذلك المصفوفات: كيفية الإعلان عنها، وفهرستها، والمرور عليها بالحلقات، وتفادي الوصول خارج الحدود الذي يسبّب الكثير من السلوك غير المعرَّف في C++.

الأسئلة الشائعة

ما هو stringstream في C++؟

إن std::stringstream هو تدفق مدعوم بـ string بدلاً من لوحة المفاتيح أو ملف. تكتب إليه بـ << وتقرأ منه بـ >>، تماماً مثل cout و cin، مما يجعله الأداة القياسية لتحليل النص وبناء السلاسل النصية في الذاكرة. وهو موجود في الترويسة <sstream>.

كيف تحوّل سلسلة نصية إلى int في C++ باستخدام stringstream؟

ضع النص في تدفق واستخرجه إلى رقم: stringstream ss("42"); int n; ss >> n;. تحقّق بـ if (ss) (أو if (ss >> n)) للتأكد من أن التحويل نجح فعلاً. في الحالات البسيطة يكون std::stoi أقصر، لكن stringstream يتألق عندما تحمل سلسلة نصية واحدة عدة قيم مختلطة.

لماذا أحتاج إلى استدعاء clear() على stringstream؟

بمجرد أن يبلغ التدفق نهاية بياناته، تبقى رايتا eof/fail مضبوطتين، وكل >> لاحق لا يفعل شيئاً بصمت. إذا أعدت استخدام نفس الـ stringstream بمحتوى جديد، فاستدعِ ss.clear() لإعادة ضبط رايات الخطأ هذه، ثم ss.str(newText) لتحميل بيانات جديدة، وإلا فستفشل القراءات بصمت.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن