الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

ثلاث استفسارات !

مغلق
بدأه Wase3 Sadr في 1 نوفمبر 2005 · 21 رد · 1,731 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم أخوتي في الله..

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

وها أنا ألجا إليكم, لعلي أجد إجابات شافية استفيد ويستفيد غيري منها..وتكتب لك في موازين حسناتكم أن شاء الله..

# الاستفسار الأول:

لنفرض أني كتبت مثل هذا البرنامج الصغير:

void main()
{
	char s[100];
	cin>> s;
	cout<< s;
}

عندما أدخل كلمة في المتغير s , ولتكن مثلاً " hi world's "..

يُخزن في المتغير كلمة " hi " فقط , وهكذا مع إي جملة لا تُخزن إي جملة بعد المسافة !

ونفس المشكلة تحصل مع المتغير string..

ماهو التفسير لهذه المشكلة ؟ وكيف أستطيع التعامل معها ؟!

# الاستفسار الثاني ( عن الـfiles ):

قمت باستدعاء ملف بهذا الشكل:

fstream OpenFile("c:\\file.txt",ios::in);

وأريد تخزين السطر كاملاً إذا كان أقل من 80 حرفاً وإذا كان أكبر من 80 حرفاً, أُخزن الـ80 حرفاً في المتغير والحروف الباقية في متغير أخر..

وقمت بعمل هذه الطريقة:

int main()
{  
	char s[1000];
	fstream OpenFile("c:\\file.txt",ios::in);
	while(!OpenFile.eof())
	{
  OpenFile.getline(s,80);
  cout<< s << endl;
  
	}	

	return 0;
}

ولكن لا يحدث شيئاً ؟ يستمر في التكرار بلا توقف..!

ولو قمت بوضع بدل الـ 80 , ألف..

OpenFile.getline(s,1000);

يأتي بالملف بشكل جيد " مع العلم أنه لا يوجد في الملف سطر أكثر من 100 حرف "

ولقد قمت بتجربة بهذا الشكل:

int main()
{  
	char s[1000];
	fstream OpenFile("c:\\hi.txt",ios::in);
	int i = 0;
	while(i != 1)
	{
  OpenFile.getline(s,80);
  cout<< s << endl;
  i++;
	}	

	return 0;
}

ويقوم بتخزين 80 حرفاً بشكل جيد !

وقمت بتجربة أخرى بجعل التكرار يعمل مرتين..

int main()
{  
	char s[1000];
	fstream OpenFile("c:\\hi.txt",ios::in);
	int i = 0;
	while(i != 2)
	{
  OpenFile.getline(s,80);
  cout<< s << endl;
  i++;
	}	

	return 0;
}

ولكن يقوم بنسخ الـ 80 سطراً الأولى فقط !

فـ يطبع فقط 80 حرفا..والسطر الأخر فراغ !؟

أين الخلل هنا ؟

# الاستفسار الثالث:

عندما قمت بجلب ملف من الـfiles, وأردت تخزينه في لينكد ليست..

ولنفرض أن الفايل يحتوي على الثلاثة السطور التالية:

looooooooooool

wooooooooooow

boooooooooooom

قمت بكتابة الكود هكذا:

int main()
{  
	list e;
	char s[1000];
	fstream OpenFile("c:\\testfile.txt",ios::in);
	int i = 0;
	while(!OpenFile.eof())
	{
  OpenFile.getline(s,1000);
  e.insert(s);
	}

	e.print();

	return 0;
}

يقوم بإنشاء ثلاث " نودات ", والتخزين فيهم أخر سطر " boooooooom " !!

مع العلم أني جربت طباعة الـ s قبل عمل له insert في النود..فـ فيطبع الثلاث سطور بشكل سليم..

OpenFile.getline(s,1000);
  cout<< s << endl;
  e.insert(s);

وهذه هي دالة الـ insert التي كتبتها:

void Editor::insert(char *s)
{
	node *p = new node(s);

	if(head == 0 )
	{
  head = tail = p;
  size++;
	}
	else
	{
  tail->next = p;
  p->pre = tail;
  tail = p;
  size++;
	}
}

وأين الخلل أيضاً ؟!..

أعتذر على الإطالة..وكثرة الأسئلة..

وأتمنى أن أجد إجابة على ما أحترت فيه..

جعلها الله في موازين حسناتكم..وجزاكم الله خيراً..

أخوكم..

#2

بالنسبة للاستفسار الأول

الكائن cin أو بمعـنى أصح معامل الإدراج << المعرف ضمن الكائن cin ، يعتبر أن المسافـة هي إنهاء للإدخال مثلها مثل زر Enter وحتى تنجح في تلافي المشكلة يوجد تابع مستقل اسمـه getline يستقبل ثلاث وسائط هـي :أولاً: اسم كائن الإدخال والثاني أسم السلسلة أو الـ string والثالث حرف الإنهاء ، فمثلاً في مثالك الموضوع ضمن الاستفسار الأول بإمكانك تعـديل طريقة الإدخال إلى هذا الشكل:

 getline (cin , s , '\n' );

بالنسبة للاستفسار الثاني:

فعذراً معلوماتي عـن الـ files لي مدة لم أراجعها ، ولكن لا تحتاج أصلاً لأن تقوم بـ while حتى تقوم بالقراءة تكفي الدالة العضو getline ..

أنظر إلى التجربة الثالثة التي قلت عـنها والتي تكون فيها شرط حلقـة الـ while عبارة عـن i لا يساوي الرقم 2 ، حيث تستمر الحلقة في الدوران مرتين ، هل تساءلت لماذا ، حسب رؤويتي للمثال المكتوب في التجربة الثالثة فأعتقد (لا أجزم لأن معلوماتي عـن الملفات هرمت) أن في المثال الأول السابق الذي يستمر في الدوران دون توقف ، أن البرنامج يصل إلى دالة getline ويقرأ الملف دون مشاكل ولكن مؤشر الملف يعـود إلى البداية ، أي أن البرنامج يعـود مرة أخرى إلى البداية ... وأنا لا أعتقد أن هذه هي المشكلة ولكن مجرد رأي .... ربما تكون المشكلة أيضاً في أن الملف النصـي الذي يقرأ منه البرنامج لا ينتهي بالحرف n\ مما يجعله يستمر في القراءة دون توقف ... الذي أقصده هـو أن عليك التفكير في هذه المشكلة لأنها ليست نقصاً في مفاهيمك البرمجية بل هي استخدام خاطئ برمجي عليك أنت تصحيحـه

بالنسبة للاستفسار الثالث:

تحديداً ليس لدي حل للمشكلة ، ولكن المشكلة كما هـو ظاهر في الكود الثاني أنها ليست في القراءة من الملف (القراءة صحيحـة ) ولكن الخطأ يكمن إما في الدالة العضو printأو الدالة insert .. هل جربت استخدام اللينكد ليست التي قمت بكتابتها دون استخدام الملفات وجربت اختبارات عليها حتى تتأكد أنها تعـمل بشكل صحيح.....

#3

بالنسبة الى الاستفسار الاول لا يهمك كل هذا استخدم الدالة ;(gets(X حيث X هو اسم المتغير المراد ادخال القيمة له من الاخر كده

الدالة gets هى نفس الدالة cin ولكن الفرق انها تقبل الكلمات الذى يوجد بها مسافات وتسجلها وتخزينها

ولكن تأكد من انك قمت بتضمين المكتبة

# include <string.h>

تم تعديل هذه المشاركة بواسطة hamada hassan ali في 1 نوفمبر 2005 في 03:28

_____________________________

C Language && Windwos_API

_____________________________

#4

الدالة gets جربتها مرة من المرات في كود بلغـة السي استخدمت فيه أيضاً الـ scanf.. ظهرت فيه نتائج غريبـة لا أدري من أين .... قرأت في أحد الكتب ولا أدري عـن صحـة هذا الكلام أن الدالة gets تقرأ من الكيبورد فوراً .. ولكن الدالة القوية scanf تقرأ من تيار الإدخال القياسي .. وأرجـو من أحد الأخـوة شرح هذا الأمر لي إن كان صحيحاً ، أيضاً أنظر مثلاً إلى هذا الكـود الطريف:

string a;
cin >> a;
cout << a;
cout << "Now\n";
cout << endl;

قرأت هذه المعلومـة في أحد الكتب ، إذا قمت بإدخال مثلاً الجملة arab team إلى المتغير a فإن الـ cin سيقوم بتخزين الكلمـة فقط arab بالتالي حينما تطلب إخراج محتويات السلسلة a فلن تطبع سوى arab ؛ السؤال هـو أين ستذهب الكلمة team سمعت أنها تبقى عالقـة في المنطقة الوسيطـة للإخراج في نهاية الكود السابق ، يقوم المعامل endl بإفراغ جميع محتويات المنطقـة الوسيطـة والتي من ضمنها team ،

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

arab
NOW
team

هل من الأعضاء من ينورني حول هذا الأمر!!!!

#5

كانت محور دراستى مع سلاسل الحروف المدمجة مع المصوفات وكيفية تحويل كل حرف من حروف السلسلة الى كلمات عشوائية وتخزينها واسترجعها

ولم يحدث اى خلل

_____________________________

C Language && Windwos_API

_____________________________

#6

حسناً سآتي بالكود الذي أخبرتك عـنه (حق الـ gets والـ printf والـ scanf ) وسترى أن به خلل

سأبحث عـنه ولا أدري هل هـو في جهازي أم في دفاتر الجامعـة ربما لن أجده ولكن صبر

#7

وجدت الكـود بسرعـة وأنا مستغرب هذا الأمر ..

أنظر:

#include <stdio.h>

struct student
{
	int m_Number;
	char m_Name[2];
	char m_address[20];
	int m_Tel;
}
;

int main()
{

	struct student TheStudent[3];
	int i=0;

	printf("Enter The Information\n\n");

	for(i=0;i<4;i++)
	{
  printf("Please Enter The Data about studetn %i", i+1);
  printf("\nHis Name:\t"); gets(TheStudent.m_Name);
  printf("His Number:\t"); scanf("%i",&TheStudent.m_Number);
  printf("His Address:\t"); gets(TheStudent.m_address);
  printf("His Telephone:\t"); scanf("%i",&TheStudent.m_Tel);
  printf("\n\n");
	}

	printf("\n\n The End");


	return 0;
}

جربته على الفيجوال سي بلس بلس دوت نت ويقوم البرنامج وقت التنفيذ بترك الدالة gets (الثانية وليست الأولى) وتنفيذ الأمر التالي دون أن ينتظر إدخال السلسلة .... لا أدري أين المشكلة ربما خطأ مني ..... راعي أن المترجمات مختلفة لذلك قد يعـمل الكود عـندك

#8

التعامل مع الدوت نت فانت فى عالم تانى خالص لم ترى كل شىء كمثل الدوال الاصلية يعنى ممكن يكون يوجد دالة داخل الدوت نت تعمل بنفس اسلوب الدالة gets

المهم متخلطش الامور ياعم انت بس وركز كده وهى حتشتغل

وياريت مش كل حاجة تتعلمها على السى والسى بلس العادية يمكن تطبيقها على الدوت نت فيما عدا الاوامر القياسية للغة

_____________________________

C Language && Windwos_API

_____________________________

#9

أخي أنا جربت هذا الأمر على الفيجوال سي بلس بلس 6

وعلى البورلاند 4.5 (والذي بالمناسبة يدعـم اللغة العربية)

وجميعها قالت نفس الشيء

بالمناسبة حتى لو قمت بتجريب الكـود على أي مترجم آخر حتى لو على الفيجوال ، فالسي بلس بلس ANSI ولن تستطيع مايكروسوفت تغيير بنيتها الداخلية

أنا أسألك هل نسخت الكـود وجربتـه بدلاً من أن تقول لي أنني في حاجـة ثانية خالص

بالمناسبة بإمكانك حتى ترجمـة كود بلغـة السي (وليس بلغـة السي بلس بلس) على الفيجوال دوت نت

وللأعضاء الآخرين هل جرب منكم أحد الكـود أم ماذا

#10

أولاً وقبل كل شئ..

شكراً من الأعماق لكما (Sultan_Althibity و hamada hassan ali )

على مساعدتكما لي, وتفاعلكم مع استفساراتي.. :)

بالنسبة للاستفسار الأول..

الدالة getline, قامت بالعمل بشكل جيد :)..ولكنها لا تقبل المتسلسلة من نوع char , فقط كائنات الـstring..

والدالة gets , قبلت المتسلسة وقامت بالعمل المطلوب :)..شكراً لكما..

بالنسبة للاستفسار الثاني..

فعلاً الدالة getline, تقوم بجلب سطر كامل بدون while..ولكن أنا أحتاج لـ جلب الملف كاملاً سطراً سطراً ولذلك أجتاج لهذا الـloop..

ولكن ما توصلت إليه حتى الآن..هو أن الدالة بهذا الشكل

getline(s,80);

الباراميتر الثاني, هو حجم المصفوفة وبما أنه السطر يوجد به حروف أكثر من الـ80 , فـأنه مع التكرار تحدث المشكلة بأنه لا يتوقف ( لماذا ؟ لا أدري )

ولكن عندما تكون الدالة بهذا الشكل:

getline(s,300);

وضعنا حجم المصفوفة بـ 300 , مع العلم أنه لا يوجد سطر به أكثر من 300 حرف..فـ النتيجة تظهر بشكل جيد, ويتم جلب السطور جميعها..

ولنحدد السؤال والمشكلة بشكل أوضح ومختصر:

عندما يوجد لدي ملف به أسطر, ولتكن بعض هذه الأسطر تحتوي على 100 حرف أو أكثر..

كيف أستطيع أن أجلب هذه الأسطر.. بحيث اجلب السطر كما هو إذا أكن أقل من عدد معين من الأحرف أنا أحدده ( وليكن 80 مثلاً )؟

وإذا كان السطر يحتوي على أكثر من 80 حرفاً, كيف أستطيع تقسميه بحيث أستطيع جلبه 80 حرفاً , 80 حرفاً إلى أن ينتهي السطر..؟

والأستفسار الأخير..

دوال الـinsert والطباعة تعمب بشكل جيد, قمت بتجربتها بالإضافة إليها والطباعة..والنتائج صحيحة..

المشكلة فقط كما ذكرت ! عندما يكون الاستدعاء من الفايل !!

احترت كثيراً بالسبب..لعلي أجد حلاً..

اتعتبكم بكثرة الاستفسار :)..شكراً لكم مرة أخرى..

وجزاكم الله خيراً..

#11

بالنسبة للاستفسار الثاني:

قمت بتجريب هذا الكـود على جهازي:

#include <fstream.h>

int main()
{  
char s[80];
fstream OpenFile("c:\\file.txt",ios::in);
while(!OpenFile.eof())
{
 OpenFile.getline(s,80);
 cout<< s << endl;
} 
char stop;
 cin >> stop;


return 0;
}

أكثر من 20 مرة بمختلف الأوضاع قمت بتغيير حجم السلسلة وكل شيء ونجح في القراءة دائماً...

انسخ الكـود الذي كتبته وقم بتجريبـه في حال عـدم نجاحـه في القراءة فهذا الأمر يعـود إلى الفايل نفسه الذي يقرأ منه البرنامج ... أعتقد أن الفايل ليس فيه حرف الإنتر أو هذا المحرف 'n\' مما يجعل دالة getline تقرأ دون أي توقف حتى وإن انتهى الملف ، أنا قمت بكتابة ملف به ثلاثة كلمات كل كلمـة مفصولة عـن الثانية بحرف 'n\' حاول القيام بما نصحتك به وستنجح بإذن الله

بالنسبة للاستفسار الثالث فاعتقادي أن المشكلة إما في سلسلة المؤشرات داخل اللينكد ليست أو في الفايل نفسه .. تأكد أن الفايل صالح للقراءة وتأكد أيضاً أنه ملف نصي وليس binary .....

#12

هل من أحد يقرأ المشاركة رقم 4 ويجرب البرنامج الموجود في مشاركة رقم 7 حتى ينتهي الرن...

ويجاوب على تساؤلاتي ... ويفسر لماذا يقوم البرنامج بتلك الحركات الغبية حين التنفيذ..

وشكراً

#13

والله ياأخى أنا جربت البرنامج عندى ولكن بيجى فى الloop ويتجاهل أى أمر من أوامر الgets

ولكن لما شيلت الgets and fscanf وإستخدمت الcin كان البرنامج تمام

ممكن يكون الخطأ فى كتابة دالة الgets (الله أعلم) أو ممكن تكون ناسى حاجة أو كتاب حاجة زيادة فى الparamters اللى بتكتب للfscanf

Muhammad Allam

Computer Science

@Resource(MappedURL="My Blog" )

#14

نفس المشكلة التي حدثت لديك أخي eng_3llam ؛ بحثت في مراجعي ... وجدت أن الدالة gets تقرأ من الكيبورد فوراً أما cin و scanf فهي تقرأ المدخلات من تيار الإدخال القياسي .... لكن لماذا يحصل هذا الأمر أو الخطأ في الكـود فلا ادري ؛ هل يمكن مثلاً أن تكون المدخلات تتداخل مع بعضها ...

#15

للرفع وأرجو الرد على استفساراتي

#16

للرفع وأرجـو الرد على استفساراتي

#17

السلام عليكم :)..

كل عام وأنتم بخير " بعد الزحمة "..

أخيراً وجدت حل, للاستفسار الثالث..:)

كانت المشكلة أنه بعد كل insert لا بد أن أضع..

char *str = new char[1000];

وتمت الإضافة بشكل سليم !..ولله الحمد..:)

.

.

وأيضاً نفس المشكلة حدثت لك يا eng_3llam وسلطان..

يتم تجاهل الـgets داخل الـloop .. !!

ولكن أن كانت خارجه تعمل بشكل سليم..

كيف يمكن التغلب على هذه المشكلة..

شاكر لكم تفاعلكم الطيب :)..

#18

لم أفهـم أخي استفسارك الجـديد أرجـو التوضيح

=============

يا شباب سأظل أقوم برفع الموضوع حتى تخذف عضويتي أو تجاوبوا على استفساراتي

#19
Sultan_Althibity كتب:
لم أفهـم أخي استفسارك الجـديد أرجـو التوضيح

=============

يا شباب سأظل أقوم برفع الموضوع حتى تخذف عضويتي أو تجاوبوا على استفساراتي

سلطان يتوعد قسم السي بامطاره بقنابل الرفع من العيار الثقيل , طالما لا يجد اجابه مبرره لاستفساراته :rolleyes:

تعرف يا سلطان ان نفس المشكله حصلت معي , وحلها المثالي الاستغناء عن scanf في البرنامج و وعندما تريد قرائة أرقام فانك تستخدم atoi و atof ....

ولكن مع البحث والتنقيب , لم اجد سبب منطقي لهذا الخطأ , بل اجد جميع الكتب عند استخدام gets لا تضع معها في الكود scanf ابدا

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

gets The function that cannot be used safely.

ولم يسكت الراي العام على هذا التقرير التعسفي و فطالبوا بايجاد الحلول الموضوعيه , فكان الناتج القرار رقم 7764 باصدار الداله fgets الحل الأمثل

Library:   stdio.h

Prototype: char *fgets(char *s, int n, FILE *stream);

Syntax:    char  Data[80];
    char *ReturnCode;
    FILE *FilePointer;

    ReturnCode=fgets(Data, 80, FilePointer);

    ReturnCode == NULL when an error occours. The EOF is 
    considered to be an ERROR!

مثال :

char buff[80] = {0}; /* Input buffer. */
fgets(buff, sizeof(buff), stdin);

مع atoi , atof , تعيش حياه سعيده آمنه :D

#20
اقتباس
سلطان يتوعد قسم السي بامطاره بقنابل الرفع من العيار الثقيل , طالما لا يجد اجابه مبرره لاستفساراته 

هذا يعتبر أصلاً من السياسة ، ألم تسمع لقول الخبير العسكري (اللي له 200 سنة في قبره) المشهور كلاوزوفيتز وهـو يقول: الحرب إحدى أدوات السياسة لا أقل ولا أكثر (فيما معـنى الكلام لا أتذكره بالضبط)...

فهـمت الكلام اللي عـن الدالة fget .. بس اللي ما فهـمته يا أخ أحمد هـو: آلية عـمل الدالة get .. سمعت أنها تقرأ من الكيبورد فوراً بينما scanf تقرأ من تيار الإدخال القياسية والأمر كذلك بالنسبة للدالة puts حيث تقوم بإرسال المخرجات فوراً إلى الشاشة بعكس الدالة printf التي ترسل مخرجاتها إلى تيار الإخراج القياسي...

ألا من منــوّر حـول هذا الحديث..

وشكراً

#21
Sultan_Althibity كتب:
يا شباب سأظل أقوم برفع الموضوع حتى تخذف عضويتي أو تجاوبوا على استفساراتي

تشرفنا أخي سلطان

أعجبني ردك كثيرا

و لوكان الكثير مثلك لكان حال المنتدى رائع

المشكلة أخي أن الكثير من المبرمجين لا يعلم تماما ما يحدث

هناك عدة حلول مثل إستخدام sscanf بدلا من scanf

#include <stdio.h>
//#include <conio.h>

struct student
{
int m_Number;
char m_Name[20];
char m_address[20];
int m_Tel;
}
;

int main()
{
char buff[80];
struct student TheStudent[3];
int i=0;


for(i=0;i<4;i++)
{
 printf("Please Enter The Data about studetn %i", i+1);
 printf("\nHis Name:\t"); gets(TheStudent.m_Name);
 printf("His Number:\t"); gets(buff);sscanf(buff, " %i ", &TheStudent.m_Number);
 printf("His Address:\t"); gets(TheStudent.m_address);
 printf("His Telephone:\t"); gets(buff);sscanf(buff, " %i ", &TheStudent.m_Tel);
 printf("\n\n");
}

printf("\n\n The End");


return 0;
}

لكن لا تريد مثل هذا الحل :D

تريد فهم المشكل، حسنا

بالنسبة للدالة gets تقرأ المدخلات من تيار الإدخال القياسي standard input

المشكلة في scanf و ليس gets

بالأحرى طريقة إستخدامك لها

scanf("%i",&TheStudent.m_Number);

في هذا الأمر تقرء رقم الطالب

المشكلة أنك عند التنفيذ و كتابة رقم الطالب وبعده مفتاحEnter

ما يعادل \n هنا مربط الفرس

هذا \n يبقى عالق وكل gets التي تلي تلتقطه و بالتالي تحصل على نهاية إدخال

الحل الأن هو حذف \n بعد كل scanf

بقرائتها:

scanf("%c", &d);

ولا تنسى إضافة متغير في بداية البرنامج

char d;

وتوجد حلول أخري، فلا حدود للخيال

الأستعمال السيئ للداله Gets يجعل برنامجك قابل Buffer OverFlow

:D

d4baa0.gif
#22

أنا قرأت كلام قبل كذا .. شبيه به عـن الـ getchar استبعـدت أن يكون له علاقـة به هـنا

مشكور أخي ... وبالفعل صراحـة حل مبدع للغاااااااااااااااااااااية

بعـد هذا الرد أعتقد أن الدلفي والسي بلس بلس أصبحا فتيات أوفياء لبعضهـم البعض (وإن كانت الأولى بنت الباسكال والثانية بنت السي)

هذا الموضوع مغلق.

مواضيع مشابهة