Salwan AlHelaly

++C المتقدمة: مكتبة STL

21 ردود في هذا الموضوع

++C المتقدمة: مكتبة STL

سلسلة دروس بقلم: ســلوان الهلالي (SandHawk)

---------------------------------------------------------

(الجزء الأول)

مقدمة لـ STL ونوع std::string

ما هي STL؟

STL تعني Standard Template Library, وهي مكتبة برمجية تمثل أحد أجزاء مكتبة ++C القياسية. توفر STL عدداً من الخدمات هي containers و iterators و algorithms و functors. أهم جزء منها هو جزء الحاويات containers والذي يقدم عددأ من حاويات البيانات العالية المستوى مثل vector و list وبما إن جميعها مكتوب بإستخدام ميزة الـ templates, فتستطيع إستخدامها لأي نوع من أنواع البيانات سواء كانت عددية أو نصية كذلك ممكن إستخدامها مع الهياكل structures والأصناف classes.

تستند مكتبة STL الموجودة في أشكال ++C المختلفة المتاحة اليوم على مكتبة STL التي طورتها شركة SGI. و STL هو جزء من الـ C++ ISO.

محتويات STL

1. الحاويات Containers

تقدم STL نوعين من الحاويات, النوع الأول يسمى بالحاويات المتعاقبة (Sequence containers) حيث تخزن القيم بشكل متعاقب كالمصفوفات مثلاً والثاني يسمى بالحاويات المترابطة (Associative containers) حيث تخزن القيم حسب روابط معينة موجودة.

1. الحاويات المتعاقبة:

  • vector

مصفوفة تشبه المصفوفات الإعتيادية ولكنها تقوم بإدارة البيانات بالكامل وهي قابلة للتوسع (عندما تضيف عناصر جديدة) أو التقلص (عندما تحذف عناصر موجودة) لذلك تعتبر بديلاًً فعالاً للمصفوفات الإعتيادية, كذلك فإن أداءه موازي للأداء الذي تقدمه المصفوفات الإعتيادية.

  • list

تخزن البيانات داخلياً كقائمة مترابطة الطرفين (double linked list) أي إن العناصر لا تخزن في عناوين متتابعة في الذاكرة لذلك فهي لا تقدم إمكانية الدخول العشوائي, تتميز بقابليتها لإضافة أو حذف أي عدد من العناصر في وقت قصير جداً, لذلك فهي مناسبة لأنظمة البيانات التي تتغير كثيراً.

  • (deque (double ended queue

مشتق من عمل الـ queue, هو حقيقة يشبه الـ vector داخلياً, ولكنه يتيح إضافة عناصر للمقدمة (قبل أول عنصر) أوالمؤخرة (بعد آخر عنصر) كذلك قراءة وحذف العناصر من المقدمة والمؤخرة, لا تقدم إمكانية الدخول العشوائي, وهي مناسبة لتطبيقات خاصة.

2. الحاويات المترابطة:

  • set

تمثل مجموعة من العناصر مرتبة حسب إسلوب محدد يعتمد على قيم العناصر والتي يجب أن تكون فريدة ولا يجوز تكرار نفس القيمة (مثلاً مجموعة من الكلمات مرتبة أبجدياً أو مجموعة أرقام مرتبة تصاعدياً أو تنازلياً), من الممكن إضافة أو حذف العناصر, كذلك تقدم عمليات متقدمة على المجموعات تتضمن الإتحاد union, التقاطع intersection, الإختلاف difference, الإختلاف المتطابق symmetric difference وإمكانية فحص الإحتواء test of inclusion, يجب أن يتم بتطبيق دالة مقارنة (أصغر من ">") لكل نوع من العناصر, داخلياً المجموعة مخزونة كشجرة ثنائية متزنة balancing binary search tree.

  • multiset

هي set مصممة بطريقة تسمح بتكرار العناصر.

  • map

وهي مصفوفة مترابطة مرتبة, تتألف عناصرها من جزئين الأول هو المفتاح (key) والثاني هو القيمة (value) حيث تقوم الـ map بالربط بين المفتاح والقيمة وكما في كل أنواع الحاويات في STL المفتاح والقيمة ممكن أن يكونا أي نوع سواءاً من عناصر رقمية, نصية, أو حتى كائنات. لذلك فهذا النوع من الحاويات مفيد جداً في مختلف أنواع التطبيقات, يجب أن يتم تطبيق دالة مقارنة لنوع العناصر المفتاحية (type of key), كذلك لا يجوز تكرار نفس القيمة للمفاتيح.

  • multimap

وهي map مصممة بطريقة لتسمح لإمكانية تكرار القيم المفتاحية.

  • hash_set / hash_multiset / hash_map / hash_multimap

مماثلة للـ multimap, map, multiset, set ولكنها مطبقه داخلياً بإستخدام hash table مما يجعل المفاتيح غير مرتبة unsorted, وكذلك دالة للـ hash يجب أن تطبق لكل نوع من المفاتيح بدل دالة المقارنة, في الحقيقة إن هذه الحاويات قد تمت إضافتها حديثاً وهي ليست موجودة في الـ C++ ISO الحالية, ولكن سيتم إضافتها للمعايير الجديدة القادمة التي ستصدر بعد سنة أو أكثر ولكن مع تغيير كلمة hash إلى unordered.

الفرق بين هذا النوع من الحاويات (نوع hash) والحاويات الإعتيادية من نفس النوع هو إن حاويات hash لا ترتب البيانات بينما الإعتيادية تقوم بترتيبها, مما يجعل hash أسرع ولكن مع فقدان عدد من الإمكانيات.

3. حاويات أخرى

  • bitset

تشبه vector بحجم ثابت ولكنها تخزن بيانات من نوع bool فقط, حيث تقوم بخزن كل bool كـ بت واحد للتقليل من الذاكرة وزيادة السرعة.

  • valarray

مصفوفة تشبه نوع vector, ولكنها مصممة خصيصاً لتكون ذات سرعة كبيرة, حيث إنها مناسبة للتطبيقات العددية المعقدة وهي مصممة لتستفيد من سرعة الـ supercomputers, ولكن في المقابل فالعمل معها معقد وصعب مقارنة مع الـ vector الإعتيادي وهي لا تستطيع أن تدير وتعمل مع قيم غير عددية.

بعض هذه الحاويات قد لا يكون مفيداً للمبرمج الإعتيادي (آخر إثنين) ولكن بقية الحاويات تجعل من إدارة مجاميع البيانات عملية سهلة, وتقدم خدمات عالية المستوى وأداء كبير, معظم البرمجيات اليوم تستخدم مكتبة الـ STL كعنصر رئيسي, والـ STL كما ذكرت هو جزء من الـ ++C لذلك لن تحتاج للربط (linking) لأي مكتبات خارجية.

2. المكرّرات Iterators

هذا الجزء مستخدم بشكل رئيس لجعل عملية قراءة عناصر الحاويات بإختلافها عملية سهلة وموحدة بين جميع الحاويات, الـ STL تطبق خمسة أنواع من المكرّرات ولا تحتاج لمعرفتها وفهمها لتستخدم STL لذلك يمكنك أن تهمل هذا الجزء, ولكني سأتكلم عنها بإختصار.

الأنواع الخمسة هي: أولاً مكررات الإدخال (Input iterators) والتي من الممكن إستخدامها لقراءة سلسلة من القيم. وثانياً هنالك مكررات الإخراج (Output iterators) والتي تستخدم فقط لكتابة سلسلة من القيم. وكذلك المكررات الأمامية (Forward iterators) والتي من الممكن قراءتها, الكتابة لها, والإنتقال للأمام (أي للعنصر التالي). وهنالك المكررات ثنائية الإتجاه (Bidirectional iterator) والتي تشبه المكررات الأمامية ولكنها تستطيع الإنتقال للخلف أيضاً. وأخيراً هنالك مكررات الدخول العشوائي (Random access iterators) والتي تستطيع أن تتحرك أي عدد من الخطوات كل مرة وفي أي إتجاه.

ما هي المكررات أصلاً؟ لنأخذ هذا المثال:

لدينا مصفوفة إعتيادية تحتوي على 10 عناصر من نوع int, كيف يمكننا دخول وخزن العناصر العشرة بالترتيب؟ ذلك بالتأكيد يتم عن طريق حلقة تكرارية تكرر من 0 إلى 9.

والآن ماذا لو كان لدينا مجموعة من القيم التي ليست مخزونة بالترتيب (مثلاً مخزونة في قائمة مترابطة linked list), كيف سنتمكن من دخول وخزن عشر عناصر بالترتيب؟ إذا كنت مطلع على ماهية القائمة المترابطة فالجواب بالتأكيد هو عمل recursion بدءاً من العنصر الأول (root) حتى العنصر الأخير.

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

المكرّرات كما ذكرت, هي أحد أهم المزايا التي جعلت حاويات الـ STL تتمتع بالعمومية (generality) أي رغم الإختلاف الكبير في طريقة تمثيل البيانات والإمكانات المتاحة فإن طريقة التعامل مع جميع أنواع الحاويات متشابهه بشكل كبير.

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

3. الخوارزميات Algorithms

مجموعة كبيرة من الخوارزميات التي توفر خدمات مثل الترتيب sorting أو البحث searching والتي تستخدمها حاويات STL نفسها, وهي مرتبطة بالمكرّرات لذلك فإن الخوارزميات ستعمل مع أي حاوية ما دامت قد طبقت واجهة للمكررات Iterator interface.

4. الدوال الخاصة Functors

ليست functor كلمة حقيقة لذلك فقد ترجمتها لـ "الدوال الخاصة" إستناداً إلى عملها.

توفر الـ STL كائنات خاصة تتيح تجاوز عامل الأقواس "( )" ( operator ) الذي يمثل إستدعاءً لدالة إعتيادية. الأصناف (classes) التي تقوم بهذه العملية تسمى بالـ functor أو function object. فائدتها تنحصر في إبقاء أو إسترجاع معلومات عن حالة الدوال التي تمرر لدوال أخرى. مؤشرات الدوال الإعتيادية يمكن كذلك إستخدامها كـ functor.

من الصعب علي شرحها لأنها ميزة غريبة صراحة, يمكن فهم كيفية عملها من المثال الآتي:

class Functor

{

public:

int operator()(int a, int b)

{

return a < b;

}

};


int main()

{

Functor f;

int a = 5;

int b = 7;

int ans = f(a, b);

}

أي إن الصنف نفسه أصبح يشبه دالة إعتيادية, هذه الميزة مستخدمة لتطبيق المكرّرات Iterators والخوارزميات Algorithms.

نوع std::string

رغم إنه لا يبدو جزءاً من الـ STL, إلاّ إنه في الواقع جزء منه ويستخدم أصلاً حاوية خاصة بالإسم basic_string التي هي في واقع الأمر تشبه حاوية من نوع vector تحتوي على عناصر char (أو wchar حسب حالة اليونيكود) , الـ std::string يسهل عمليات التعامل مع النصوص (التي هي *char) بشكل كبير, عندما إستخدمته شخصياً لأول مرة لم أعتقد إنه سيشكل فرقاً كبيراً, ولكني كنت مخطئاً, أخي المبرمج/المبرمجة, إذا كنت لا تزال تستخدم *char لتمثيل النصوص في ++C فقد حان وقت التغيير!

جميع المبرمجين الذين إستخدموا سابقاً *char للنصوص يعلمون التعقيدات والصعوبات التي تتخلل العمليات النصية في برامجهم, هنالك الكثير من العيوب لـ *char, ولكن std::string لا يحاول أن يحل محل *char, ولكنه يقوم بعملية "تغليفه" بطريقة تسهّل كثيرأ العمل مع النصوص.
وتستطيع في أي لحظة التحويل بين *char و string بسهولة كبيرة.

إستخدام string

الملف الرأسي الذي يحتاجه string هو من دون ".h":

[

#include <string>

using namespace std; // To get rid of "std::"

والآن أنت جاهز لإستخدام string!

[

string s1 = "Hello World!";

لاحظ أن string تستطيع أن تقوم مباشرة بتحويل نوع *char إلى التمثيل الداخلي لها, الكود التالي يحوّل من *char إلى string ثم بالعكس ثم يظهر الإثنين:

char* c1 = "Hello World!";

string s1 = c1; // Simple!

// string::c_str() will return the char* string.

char* c2 = s1.c_str();

// output each to console:

cout<<"The c string: "<<c1<<endl;

cout<<"The std::string: "<<s1<<endl;

// Or

cout<<"The std::string: "<<s1.c_str()<<endl;

دالة c_str تعيد تمثيل *char للنص, أي لكي تحصل على أو تمرر إلى دالة نص إعتيادي *char يمكنك مباشرة إستخدام ()string::c_str.

والآن حان وقت العمليات التي تستطيع الـ string القيام بها, فلنبدأ أولاً بعملية دمج النصوص (concatenation) لنفترض أن لدينا نص من نوع string نريد دمج نصين آخرين أحدهما من نوع string أيضاً والآخر من نوع char* ثم نظهر النص الناتج:

[

string s_out = "Ready!";

string s1 = " ,Get set!";

char* c1 = " ,Go!!!";

// concatenate all into s_out

s_out += s1; // or s_out += s1.c_str();

// or s_out = s_out + s1;

s_out += c1; // or s_out = s_out + c1;

// output to console

cout<<s_out();

//--------------------------------------------

// Or you can directly concatenate all strings:

s_out = "Ready!";

s_out += s2 + c1;

cout <<s_out();

]

إذن كما هو ملاحظ, string تقدم مرونة كبيرة وتسهل العمل كثيراً.

لتدخل الآن في التفاصيل, هنالك نوعان من string, الأول هو الإعتيادي "string" والذي يغلف نص من نوع *char والثاني "wstring" والذي يغلف نص من نوع *wchar_t.

ننتقل الآن للعوامل (operators) التي تقوم string بتعريفها:

  • العامل "=!" (لا يساوي): يفحص ما إذا كان النص string على الجانب الأيمن لا يساوي النص string على الجانب الأيسر (يجب أن يكون الطرفان من نوع string)

string s1 = "This is Me";

string s2 = "This is not me";

if( s1 != s2 )

cout<<"s1 is not equal to s2."<<endl;

  • العامل "==" (يساوي): يفحص ما إذا كان النص string على الجانب الأيمن يساوي النص string على الجانب الأيسر (يجب أن يكون الطرفان من نوع string)

  • العامل ">" (أصغر من): يقوم بعملية مقارنة إستناداً إلى الكلمات والحروف, إذا وجد حرفان مختلفان في نفس الموقع من النصين سيكون ناتج المقارنة مستند على قيمة الحرفان, أما إذا وجد إن النصان متشابهان ولكن أحدهما يمتلك حروف أو كلمات إضافية سيعتبر النص الأول هو الأصغر, إذا لم يجد أي إختلافات فالنصين متطابقين.

      • العامل "

  • العامل "=>" (أصغر من أو يساوي): نفس عمل عملية الأصغر من, ولكنها تعيد "true" عند حالة التساوي أيضاً.

  • العامل "=

  • العامل "+": يقوم بعملية دمج نصين.

      • العامل "[ ]": يتيح إمكانية دخول عناصر النص كمصفوفة إعتيادية:


// This will output the first character 'H' then 'W':

cout<<s1[0]<<s1[6]<<endl;
string s1 = "Hello World!";

الآن جاء دور الدوال التي تقدمها std::string

  • swap: تقوم بأخذ نصين من نوع string لكي تبدل النص بينهما, أي النص الأول ينتقل للثاني والثاني للأول.

      • getline: يستخدم مجرى إدخال (input stream) لإدخال سطر من النص للـ sting.

std::string ترث صفاتها من basic_string وهي الحاوية التي تصف نص بشكل عام, أي نوع من النصوص من غير تحديد, لذلك فإن string ورثت دوال basic_string, سأقوم هنا بذكرها والتكلم عن المهم منها, ولكنك لن تحتاج في أغلب الأحيان إلى أي من هذه الدوال, عدا c_str و length:


  • append
  • assign


  • at: تعيد مرجع reference للحرف الموجود في at من النص.


  • begin


  • c_str: تعيد النص الداخلي الذي هو بصيغة *char والمنتهي بـ NULL.


  • capacity: تعيد أكبر حجم للنص ممكن خزنه في كائن string الحالي من دون الحاجة إلى إستخدام ذاكرة إضافية (no extending memory).


  • clear: تمحي النص الموجود بالكامل.


  • compare


  • copy


  • data


  • empty: تفحص ما إذا كان النص فارغ أم لا, تعيد "true" إذا كان النص الحالي فارغ أو "false" إذا لم يكن.


  • end


  • erase: تقوم بمسح حرف أو مجموعة محددة من الحروف من النص.


  • find: تبحث عن نص محدد بدءاً من بداية النص, في حالة العثور على النص المطلوب تعيد فهرس index أول حرف من النص المطلوب, أما إذا لم تعثر على شئ فسوف تعيد "npos".


  • insert: تضيف حرف أو نص إلى الموقع المحدد.


  • length أو size: تعيد عدد الحروف الموجودة في النص.


  • max_size: أقصى عدد من الحروف يستطيع كائن string أن يتعامل بها (عدد كبير يساوي عادة
    4294967294
    أي ما يساوي 4 تيرابايت!!).


  • push_back


  • replace: تبدل النص المحدد بالنص الذي يتم تمريره.


  • reserve


  • resize: تحدد حجم جديد للنص, إذا كان الحجم المحدد أقل من النص الموجود سيتم حذف الحروف الزائدة.


  • substr


مثال يبين كيفية تشكيل نص بإستخدام sprintf وتحويله إلى string:

char buf [255];
int n = 1505453;
sprintf( buf, "Your ID number is: %d", n );
string s1 = buf;

الآن, المثال السابق نفسه ولكن بإستخدام المجاري النصية stringstream والتي هي أيضاً جزء من المكتبة القياسية للـ ++C, عمل stringstream مماثل لعمل cout و cin, ولكن بدل الإخراج للشاشة والإدخال من أجهزة الإدخال (لوحة المفاتيح تحديداً) فإنها تقوم بالإخراج والإدخال إلى نص من نوع string بنفس الطريقة تماماً:

#include <sstream>
#include <string>
using namespace std;
//...
stringstream ss;
string s1 = "Your ID number is: ";
int n = 1234124;
ss<<s1; // Output s1 to the string stream
ss<<n; // out put n to the string stream
ss>>s1; // input the contents of the stream into
// s1 string.
// Or you can do it directly:
ss<<s1<<n<<endl;
ss>>s1;
// Output to screen
cout<<s1;

إستنتاج

العمل مع std::string يسهل التعامل مع النصوص في ++C إلى درجة لأنه يقدم واجهة عالية المستوى, وعبر تغليف *char فإن string مناسب تماماً لجميع إستخدامات *char, كذلك يمكنم إستخدام كائن الـ stringstream لأداء عمليات إخراج وإدخال أنواع مختلفة من البيانات من وإلى الـ string.

ببساطة... string تجعل الحياة أسهل!

الدرس القادمة إن شاء الله:

الجزء الثاني: الحاوية std::vector

------------------------------------------------------------------

تم تعديل بواسطه SandHawk
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

مشكور اخي ... عمل رائع وجميل

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

تسلم إيديك انا كنت لسه هاشتكي لكم من ال stl

انا عمال اذاكر فيها بقال فترة بس بالإنجليزي والحمد لله فهمتها بس كنت محتاج حد يشرحها بالعربي لزيادة الفهم والله كتر خيرك

بس انا عندي سؤال هو سابق لأوانه بس محتاجه ضروري

انا علي مشارف الإنتهاء من stl ولما رحت لل container وجدت انها تعتبر تؤدي نفس الغرض انا بقول تعتبر يعني فيه إختلافات بسيطة طب ليه مخلوهاش حاجة واحده وريحونا يعني القوائم المرتبطة تعتبر أشملهم وأعمهم طب ما نمشي مع القوائم المرتبطة ونفض للباقي

ملحوظة :

انا أول مرة دي أذاكر فيها ال stl وهياكل البيانات يعني إحتمال يكون فيه أخطاء في كلامي ياريت السماح والتوضيح

بس الشرح هااااااااااايل

تم تعديل بواسطه Dark Night
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

كما شرحت أعلاه, لكل من الحاويات المتوفرة مبدأ عمل مختلف وقدرات مختلفة مما يؤدي إلى إن لكل منها إستخدامات مختلفة, لك مطلق الحرية إذا كنت تريد أن تعمل فقط على list أو vector, وتهمل الباقي.

أما ما تقوله إن الـ list تعتبر أشملهم وأعمهم فذلك غير صحيح. أبسط شئ لا تقدمه القائمة المترابطة هو الدخول العشوائي Random Access. أي مثلاً لو كان لديك قائمة مترابطة تحتوي على 10000 عنصر وتريد أن تقرأ العنصر الـ 9000 سيكون عليك (أو على STL في حالة إستخدتمك لـ list) القيام بعملية recursion لـ 9000 مرة حتى تصل إلى ذلك العنصر! وذلك وقت بحث طويل جداً.

أما لو إستخدمت vector ووضعت في داخله 10000 عنصر, تستطيع الدخول إلى العنصر 9000 هكذا [my_vector[9000 ببساطة, وزمن البحث سيكون صفر! وهذا هو معنى الـ Random Access.

مع ذلك فأنا لا أستطيع أن أقول إن vector أفضل من list فقط لأنه يدعم الدخول العشوائي. حيث إن للقائمة المترابطة ميزاتها أيضاً كما لبقية أنواع الحاويات.

يبقى أن أقول إن كل هذه أدوات, ليس المهم تسميتها وكيفية عملها, ولكن المهم هل ستستفيد منها في عملك؟ بالنسبة لـ STL, فبالتأكيد بغض النظر عن كونها جزء من ++C أم لا.

أما عن الصعوبة, فإن STL ليست "صعبة" بمعنى الكلمة, ولكنها قد تحتوي على بعض التعقيدات.

هناك مكتبات أعقد بمراحل, ولكنها مع ذلك مستخدمه على نطاق واسع لفائدتها, مثل Boost.

أنا حاضر لأي إستفسار آخر.

:)

1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

درس ممتاز ,,

لدي أسئلة حول string .. لاني أمضيت عمري " البرمجي " كله على char* :) ..

اذا كتبت هذا البرنامج البسيط ستظهر أخطاء .. ماهو الحل .

int main()
{
wstring unicode = L" UniCode ";
//cout << " unicode " << unicode <<endl;
cout << " unicode " << unicode.c_str <<endl;


return 0;
}

سؤال آخر ..

لو فرضنا عندي نوعين من الstring واحد ascii والاخر unicode .

هل توجد طريقة مباشرة للتحويل من أحد الأنواع الى النوع الاخر ؟ .. مثال /

	string  ascii = "ASCII";
wstring unicode = L" UniCode ";

ascii = unicode; //error !!

تم تعديل بواسطه الشمري
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أهلاً بالأخ الشمري :)

بالنسبة للسؤال الأول:

الطريقة unicode.c_str تعمل ولكنها تظهر أرقاماً بدل النص, وذلك لأن cout المستخدمة مخصصة لإخراج النصوص من نوع ASCII.

لكي تستطيع أن تقوم بإخراج wstring إستخدم دالة wcout:

wcout << " unicode " << unicode <<endl;

بالنسبة للسؤال الثاني:

ليس هنالك طريقة مباشرة للتحويل, ولكن تستطيع إستخدام دالة wcstombs و mbstowcs من stdlib.h أو تستطيع إستخدام الدالتين MultiCharToWideChar و WideCharToMultiChar من windows.h.

مثال لدالتين الأولى تقوم بالتحويل من wstring إلى string والثانية بالعكس:

// Remember including windows.h
string convertString( wstring &wstr )
{
int length = wstr.length()+1;
// Get the required size of char to recieve the converted string
int len = WideCharToMultiByte( CP_ACP, 0, wstr.c_str(), length, 0, 0, 0, 0 );
char* buf = new char[len];
// Now that we have a buffer let's do the actual conversion
WideCharToMultiByte( CP_ACP, 0, wstr.c_str(), length, buf, len, 0, 0 );
string r(buf);
delete [] buf;
return r;
}

wstring convertString(string &str)
{
int length = str.length()+1;
// Get the required size of wide char to recieve the converted string
int w_len = MultiByteToWideChar( CP_ACP, 0, str.c_str(), length, 0, 0 );
wchar_t* buf = new wchar_t[w_len];
// Now that we have a buffer let's do the actual conversion
MultiByteToWideChar( CP_ACP, 0, str.c_str(), length, buf, w_len );
wstring r(buf);
delete [] buf;
return r;
}

ولكن الطريقة الأفضل هي أن تتلافى إستخدام نوعين من النصوص في برنامج واحد, ممكن إستخدام TCHAR لتحقيق هذا, عن طريق إنشاء نوع جديد من string فلنسميه tstring:

// ...
#include <string>
#include <tchar>
// Define new string type based on the container basic_string:
typedef std::basic_string<TCHAR> tstring;

والآن تستطيع إستخدام tstring كـ string إعتيادية بغض النظر عن نوعها.

أتمنى أن أكون قد أجبت عن إستفساراتك :)

تم تعديل بواسطه SandHawk
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

جزاك الله خير على الرد ,والكود يعمل بشكل ممتاز .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

لم أتطرق بعد لل STL ، ولكن أعتقد أنها تعتمد على operator over loading,RTTI,inhertance ، يعني على أشياء متقدمة ، لذلك لابد من اتقان هذه الأجزاء قبل الدخول في هذه الدروس

هذه نصيحة للقراء.

وأرجوا من الأعضاء عدم كتابة ردود عبارة عن شكر فقط ، (طبعا أنا لا أنكر أهمية الشكر) ولكن ليكن مع كلمة الشكر سؤال ، فهذا أبلغ معاني الشكر لأنه يدل على أنك اهتممت بما كتب وفكرت به وبالتالي كانت الثمرة أنك لديك سؤال.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
السلام عليكم

لم أتطرق بعد لل STL ، ولكن أعتقد أنها تعتمد على operator over loading,RTTI,inhertance ، يعني على أشياء متقدمة ، لذلك لابد من اتقان هذه الأجزاء قبل الدخول في هذه الدروس

هذه نصيحة للقراء.

وأرجوا من الأعضاء عدم كتابة ردود عبارة عن شكر فقط ، (طبعا أنا لا أنكر أهمية الشكر) ولكن ليكن مع كلمة الشكر سؤال ، فهذا أبلغ معاني الشكر لأنه يدل على أنك اهتممت بما كتب وفكرت به وبالتالي كانت الثمرة أنك لديك سؤال.

وعليكم السلام ورحمه الله وبركاته :)

يؤسفني إني لا أوافقك على الجزء الأول.

STL لا تستخدم Inheritance بشكل كبير, ولكن أهم جزء مبنية عليه STL هو الـ templates (وذلك يظهر من تسميتها Standard Template Library) ولا أعتقد إنك ستحتاج أن تتقن أي شئ عدا لغة ++C الإعتيادية وإلا فما فائدة STL لو كان الأمر هكذا؟ أنا نفسي مثلاً لا أتقن الـ templates ولم أستخدمها سابقاً ولكني مع ذلك أستخدم STL كثيراً.

وأقول مرة أخرى قد تبدو صعبة حتى تستخدمها في عمل فعلي, ستكتشف إنها أسهل وأقوى من الطرق التي إستخدمتها سابقاً, مثلاً string أفضل من إستخدام *char, وvector أفضل من إستخدام مصفوفات C الإعتيادية.

وأوافقك على عدم كتابة ردود للشكر.

وشكراً

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

تحياتي لك أستاذي الكريم ...

شكرا جزيلا على هذا الدرس الجميل . ولكن لي بعض التعقيبات عليه ان لم تمانع

لماذا استخدمت

char* c1 = "Hello World!";

أعتقد انك تعلم أن أفضل طريقة لتجنب أخطاء الكمبايلر هي استخدام ("strcpy(c1, " Helloworld)

تحياتي .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

شكراً لمداخلتك,

في الكود strcpy يجب على المبرمج نفسه أن يقوم بحجز الذاكرة سواءأ من الـ heap أو الـ stack, وعادة يكون هذا الأمر تقريبياً, وبما إني لا أتكلم في موضوعي عن كيفية إستخدام char فقد إخترت الطريقة الأسهل والأوضح, والتي تترك أمر حجز الذاكرة للكومبايلر حيث سيقوم بحجزها من الـ stack, ولكن أسألك, ما هو الخطأ الذي تتكلم عنه؟ لأن vc6 لم يعترض ولا حتى بـ warning, وليس هنالك خطأ منطقي, ممكن أن أكون غافلاً عن شئ ما؟

كما إن هنالك إسلوب ثاني مكافئ لكتابة نفس الشئ:

char c1 [] = "Hello World!";

والإسلوبان كما قلت يحجزون ذاكرة من الـ stack, لذلك فليس هنالك خطر لتسرب في الذاكرة (Memory Leak).

أنتظر جوابك :)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أسف أستاذي العزيز .... اعتقدك تتكلم عن c++ وليس vc++ .

على العموم الاخطاء التي يمكن أن تحدث هي في هذه الجملة بالتحديد وذلك لعدم قدرة عمل initialization لل char بهذه الطريقة .

وشكرا .

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

فعلاً, أنت على حق في ذلك, رغم إنه لا يظهر أي خطأ ولكن هنالك إمكانية حدوث خطأ, مع إنه ليس بسبب الـ intiailization.

عند القيام بعملية initialize لـ *char بالطريقة التي إستخدمتها في المقالة سيقوم الكومبايلر بحجز الذاكرة المطلوبة من data segment كذاكرة قراءة فقط read only, ذلك بحد ذاته لا يسبب خطأ, ولكن الخطأ يحصل عندما يقوم المبرمج بمحاولة تغيير أحد عناصر char بالدخول المباشر, مثلاً c1[0] = some new value سيحصل خطأ حينها من نوع segfault بسبب محاولة الكتابة إلى ذاكرة من نوع قراءة فقط, من الصعب العثور على خطأ من هذا النوع.

لتلافي هذه المشكلة من الممكن وضع const قبل char, بهذه الطريقة سنتأكد إننا لن نحاول بالخطأ دخول النص حيث سيمنع الكومبايلر مقدماً أي عملية دخول للكتابة:

const char* c1 = "Hello World!";

شكراً لتنبيهي م. طارق عودة, لقد كنت فعلاً غافلاً عن ذلك *-)

ربما ذلك سبب آخر لإستخدام std::string بدل char

:D

تم تعديل بواسطه SandHawk
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

مشكور اخي على الموضوع .

لي عوده بعد ما اقراءه .

مع التحيه

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

أخي العزيز الموضوع رائع ولكن ليتك تعرض بعض الاسئلة في نهاية موضوعك عن الموضوع نفسه يرحمك الله .

اخوك في الله .

-waf

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

اك جزاء الله صاحب الدروس .

وكل المشاركين على التفاعل .

مشكورين .

هذا موقع مفيد بمعنى الكلمه .

مع التحيه

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكــم ورحمـة الله وبركاتــه ،،

up

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

بارك الله فيك أخي الكريم

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

جزاك الله خير اخي الكريم ووفقنا وياك جميعا

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .