العبارات النصية في ++C

العبارات النصية

تعامل العبارات النصية strings في لغة السي كمصفوفات عناصرها من النوع المحرفي char وكأي من أنواع البيانات , قد تكون هذه العبارات ثابتة أو متغيرة

متغيرات العبارات النصية

إليك برنامج يسأل المستخدم إدخال عبارة نصية , ثم يظهرها له علي الشاشة :

stringin.cpp


 


// stringin.cpp


// simple string variable


#include <iostream.h>


#include <conio.h>


const int MAX = 80;                 // max characters in string


 


void main()


   {


   char str[MAX];                   // string variable str


 


   cout << "\nEnter a string: ";


   cin >> str;                      // put string in str


   cout << "You entered: " << str;  // display string from str


   getche();


   }




في هذا البرنامج كان تعريف متغير العبارة النصية كتعريف أي مصفوفة عادية , نوع عناصرها محرفي , وحجمها 80 . وقد استخدمنا المؤثر >> لكي ندخل الحروف من لوحة المفاتيح إلي المصفوفة , ويبين المصفوفة , ويبين الشكل العبارة النصية في الذاكرة .



Object-Oriented Programming in C   _Page_0317_Image_0001











شكل عبارة نصية مخزنة



يحتل كل محرف بايتا واحدة كما سبق قوله , ولكن هناك خصيصة هامة في لغة السي , وهي أن العبارة النصية تنتهي دائما ببايت تحتوي علي الصفر , ليدل علي نهايتها , ويمثل عادة بالحرف '\0' , ويسمي المحرف الصفري null character . وحينما يظهر المؤثر << العبارة , فإنه يظل مستمرا في إظهار الحروف إلي أن يلتقي بالمحرف الصفري .



تلاقي تجاور حجم المصفوفة



سبق أن تحدثنا عن خطورة تجاوز حجم المصفوفة , ويمكن تلاقي ذلك عن طريق استخدام الدالة set() بعد أن تغذي بالحجم الأقصي للمصفوفة . والبرنامج التالي يستخدم هذه الفكرة .




safetyin.cpp


 


// safetyin.cpp


// avoids buffer overflow with cin.width


#include <iostream.h>


#include <iomanip.h>                // for setw


#include <conio.h>


 


const int MAX = 20;                 // max characters in string


 


void main()


   {


   char str[MAX];                   // string variable str


 


   cout << "\nEnter a string: ";


   cin >> setw(MAX) >> str;         // put string in str,


                    // no more than MAX chars


   cout << "You entered: " << str;  // display string from str


   getche();


   }




ولن تدخل الدالة setw(MAX) في المصفوفة حروفا أكثر من المحدد بها . بل الحقيقة أنها تحدد عدد الحروف المخلة بأقل ممن العدد المبين في معاملها بواحد , حيث يكون البايت الأخيرة مشغولة بالمحرف الصفري .



ثوابت العبارة النصية



يمكنك استهلال العبارة النصية بعبارة ثابتة , كما نبين في البرنامج التالي :







strinit.cpp


 


// strinit.cpp


// initialized string


#include <iostream.h>


#include <conio.h>


 


void main()


   {


   char str[] = "Farewell! thou art too dear for my possessing";


   cout << str;


   getche();


   }





وتلاحظ أن الاستهلال قد تم بعلامتي التنصيص المعتادين بينما استهلال المصفوفات يكون بضم عناصرها بين قوسين حلزونين , فكان المفروض أن يكون الاستهلال علي الصورة :



Char str[] = { 'F', 'a', 'r', 'e', …….};



هذه الميزة وضعها مصممو لغة السي تسهيلا لعملية إدخال الثوابت النصية . ولا تنس أن الحرف الأخير سيكون الصفر.



العبارة متعددة الكلمات



لو حاولت في البرنامج stringin.cpp إدخال عبارة مكونة من عدة كلمات , فسوف تخرج لك الكلمة الأولي قط . والسبب أن مؤثر الإدخال يعتبر المسافة إنهاء للإدخال . ولإدخال عبارة نصية متعددة الكلمات , تستخدم دالة أخري هي get() , وهي دالة منتمية , اسم المصفوفة وحجمها , علي النحو التالي :



Cin.get(str,MAX);



والبرنامج التالي يستخدم هذه الخاصية :






blanksin.cpp


 


// blanksin.cpp


// reads string with embedded blanks


#include <iostream.h>


#include <conio.h>


 


const int MAX = 80;                 // max characters in string


 


void main()


   {


   char str[MAX];                   // string variable str


 


   cout << "\nEnter a string: ";


   cin.get(str, MAX);               // put string in str


   cout << "You entered: " << str;  // display string from str


   getche();


   }




وتلاحظ أن الخلط بين الإدخال باستخدام الدالة cin.get() والطريقة المعتادة >> cin يسبب مشاكل جسيمة سوف , نتعرض لها في الفصل الحادي عشر , حيث أن معالجتها الأكبر هو البرامج الرسومية .



العبارة متعددة الأسطر



إذا كانت العبارة تحتوي علي أكثر من سطر , هنا تدخل الدالة cin::get() لإنقاذ الموقف أيضا , فهي يمكنها تتقبل معاملا ثالثا , يمثل نهاية الإدخال , والوضع المبدئي لهذا المعامل هو السطر الجديد (\n) , فإذا لم يدخل معامل ثالث تنهي الدالة الإدخال بمجرد الضرب علي مفتاح الإدخال سوف يقوم بعمله الطبيعي في النقل لسطر جديد , ولن ينتهي الإدخال إلا بالضرب علي الحرف المحدد للمعمل الثالث , ثم الضرب علي مفتاح الإدخال , وإليك تطبيق لذلك :






linesin.cpp


 


// linesin.cpp


// reads multiple lines, terminates on '$' character


#include <iostream.h>


#include <conio.h>


 


const int MAX = 2000;                // max characters in string


char str[MAX];                       // string variable str


 


void main()


   {


 


   cout << "\nEnter a string:\n";


   cin.get(str, MAX, '$');           // terminate with $


   cout << "You entered:\n" << str;  // display string from str


   getche();


   }




وفي هذا البرنامج يتسبب الضرب علي مفتاح الإدخال الانتقال إلي سطر جديد , ولإنهاء البرنامج يضرب علي حرف $ ثم مفتاح الإدخال .



نسخ العبارات النصية – الطريقة الصعبة



أفضل طريقة لفهم الطبيعة الحقيقة للعبارات النصية هي التعامل معها محرفا بعد الأخر . وهاك برنامج يفعل ذلك :






strcopy1.cpp


 


// strcopy1.cpp


// copies a string using a for loop


#include <iostream.h>


#include <string.h>       // for strlen()


#include <conio.h>


 


const int MAX = 80;       // size of str2 buffer


void main()


   {                                   // initialized string


   char str1[] = "Oh, Captain, my Captain! "


         "our fearful trip is done";


 


   char str2[MAX];                     // empty string


 


   for(int j=0; j<strlen(str1); j++)   // copy strlen characters


      str2[j] = str1[j];               //    from str1 to str2


   str2[j] = '\0';                     // insert null character


   cout << endl;


   cout << str2;                       // display str2


   getche();


   }




في هذا البرنامج نسخت محتويات المصفوفة str1[] في المصفوفة str2 باستخدام الدوارة المبنية , وفيها الأمر :



Str2[j] = str1[j];



وتلاحظ من خرج البرنامج أن محول الصياغة هنا قد وصل العبارتين النصيتين في عبارة واحدة .



ولما كانت لغة السي لا تتضمن العبارات النصية كأحد أنواع البيانات ] كما في لغات أخري [ , وبالتالي لا توجد مؤثرات تتعامل معها , فإنه توجد دوال مكتبية تقوم بهذا الغرض , وهي موجودة في ملف string.h ويقدم البرنامج أحد هذه الدوال ؛ strlen() , ] إختصار لـــ [ string length , وهي تعيد طول العبارة النصية , قد وضعت في الدوارة لتحدد لها وقت انتهائها . وتطلب استخدام هذه الدالة تضمين البرنامج الملف المكتبي المشار إليه .



ويجب أن تنهي النسخة من العبارة في المصفوفة الثانية بالمحرف الصفري تحديدا لنهايتها , وقد تم هذا بالأمر :



Str2[j] = '\0';



وفي حالة عدم تنفيذ ذلك , فإن المؤثر << سوف يظل يقرأ بقية أماكن الذاكرة المخصصة للعبارة النصية ] المحددة بالعدد المخصص للثابت MAX [ طالما لم يصادف المحرف الذي يدل علي إنتهاء العبارة , ويظهر ما بهذه الأماكن من أحرف عشوائية .



نسخ العبارات النصية – الطريقة السهلة



شكرا للدوال المكتبية في ملف string.h , فهي تضم دالة تغنينا عن هذا العناء , وهي الدالة strcpy() ] لعلك فهمت أن اسمها اختصار لـــ string copy [ , ومهمتها نسخ العبارات النصية , وسوف نعدل البرنامج السابق للإستفادة بهذه الإمكانية .






strcopy2.cpp


 


// strcopy2.cpp


// copies a string using strcpy() function


#include <iostream.h>


#include <string.h>       // for strcpy()


 


#include <conio.h>


const int MAX = 80;       // size of str2 buffer


void main()


   {


   char str1[] = "Tiger, tiger, burning bright\n"


                 "In the forests of the night";


 


   char str2[MAX];                     // empty string


 


   strcpy(str2, str1);                 // copy str1 to str2


   cout << endl;


   cout << str2;                       // display str2


   getche();


   }




وكما تري تأخذ الدالة معاملين هما اسم المصفوفة النسخ فيها , واسم المصفوفة المطلوب النسخ منها وأنها تقوم بإضافة المحرف الصفري تلقائيا .



مصفوفات العبارات النصية



طالما يوجد مصفوفة لمصفوفة , فيوجد إذن مصفوفة للعبارات النصية , ولعلك توقعت أن تكون مصفوفة ثنائية الأبعاد . وسوف نقدم برنامجا يظهر أيام الأسبوع , كتطبيق علي هذه الإمكانية المفيدة .






straray.cpp


 


// straray.cpp


// array of strings


#include <iostream.h>


#include <conio.h>


 


const int DAYS = 7;             // number of strings in array


const int MAX = 10;             // maximum size of each string


 


void main()


   {                            // array of strings


   char star[DAYS][MAX] = { "Sunday", "Monday", "Tuesday",


                "Wednesday", "Thursday",


                "Friday", "Saturday"  };            


   for(int j=0; j<DAYS; j++)    // display every string


      cout << star[j] << endl;


   getche();


   }




ولعلك لاحظت شيئا غير طبيعي في عبارة الوصول لعناصر المصفوفة :



Star[j];



حيث استخدم دليل واحد , رغم أن المصفوفة ذات بعدين , فكيف يستدل علي موضع العنصر دون الدليل الثاني ؟ الرد هو أن الدليل الأول يشير للمصفوفة الخارجية , وهذا كاف لمعرفة محتواها فهو مثبت بداية في عملية الاستهلال .

التسميات: