تابع scanf()
در زبان C دادههاي ورودي ميتوانند به كمك تابع كتابخانهاي scanfاز طريق دستگاه ورودي استاندارد وارد كامپيوتر شوند. تابع scanfنيز تابع فرمتدار و مشابه تابع printfاست ولي در جهت عكس عمل ميكند. به كمك اين تابع ميتوان دادههاي عددي، كاراكترها، رشتهها يا تركيبي از آنها را وارد كامپيوتر كرد. فرمت اين تابع مشابه فرمت تابع printfو فرم كلي آن به صورت زير است.
کد:
scanf ("control string", arguments list) ;
يا
scanf ("control string", argl , arg2 ,…, arg n) ;
در اينجا نقش رشتة كنترل مشابه تابع printfو شامل اطلاعات قالببندي خاص است. مشابه printfاين تابع نيز ميتواند هر تعداد آرگومان را دارا باشد، كه در آن اولين آرگومان رشتة فرمت يا رشتة كنترل است. همچنين اين تابع، اغلب همان كد فرمت تابع printf را به كار ميبرد؛ براي مثال كدهاي فرمت %s, %c , %f , %d که به ترتيب براي خواندن دادههايي از نوع مقادير صحيح، اعشاري، كاراكتر و رشته به كار میروند. تفاوت مهم بين اين دو تابع آن است كه در جلوي آرگومانها، اپراتور آدرس يعني "&" نيز قرار ميگيرد.
البته اگر بخواهيد مقداري را براي متغير رشتهاي بخوانيد، نيازي به اپراتور "&" نخواهد بود زيرا رشتهها در زبان ِC به صورت آرايهاي از نوع كاراكتر معرفي ميگردند و نام آرايه نيز معرف آدرس آرايه (يعني آدرس اولين عنصر آن) است.
مثال 3ـ8 برنامة زير نحوة کاربرد عملگر & را در تابع scanf نشان ميدهد.
کد:
#include
main ()
{
int x ;
char name[6] ;
scanf("%d" , &x) ;
scanf("%s", name) ;
printf("%d %s", x , name) ;
}
دستور scanf اول سيستم را هدايت ميكند كه داده ورودي را به صورت عدد صحيح از طريق ترمينال دريافت كند و اين مقدار را در متغير x ذخيره کند. دستور scanf دوم به دليل استفاده از آرايه، بدون عملگر & به کار میرود و اگر در اين برنامه براي متغير name رشتة "book" را وارد كرده باشيم، خروجي آن كلمة book خواهد بود.
جدول 3ـ3 فرامين يا کاراکترهاي فرمت براي دادههاي ورودي را كه کاراکترهاي تبديل نيز ناميده ميشوند نشان میدهد.
جدول 3ـ3 كاراكترهاي فرمت در تابع scanf ()
شـــرح
كد فرمت
داده ورودي به صورت تككاراكتر تعبير ميشود.
داده ورودي به صورت عدد صحيح علامتدار (در مبناي 10) تعبير ميشود.
داده ورودي به صورت عدد صحيح علامتدار تعبير ميشود.
داده ورودي به صورت عدد صحيح بدون علامت دهدهي تعبير ميشود.
داده ورودي به صورت عدد صحيح اعشاري با مميز شناور (floating_point) تعبير ميشود.
داده ورودي به صورت عدد صحيح كوتاه (short integer) تعبير ميشود.
داده به صورت رشته تعبير ميشود. ورودي با يك كاراكتر non_white_space آغاز ميگردد و با اولين كاراكتر white_space خاتمه ميپذيرد (به پايان رشته به طور خودكار كاراكتر "\0" افزوده خواهد شد).
داده ورودي به صورت عدد صحيح در مبناي 8 تعبير ميشود.
داده ورودي به صورت عدد صحيح در مبناي 16 تعبير ميشود.
داده ورودي اشارهگر تعبير ميشود.
مثال 3ـ9 برنامة زير يك خط متن حداكثر به طول 79 كاراكتر را ميخواند و آن را به همان صورت چاپ ميكند.
کد:
#include
main () /* read a line of text */
{
char line[80] ;
int count , k ;
/* read in the line */
for (k=0 ; line[k]=getchar ()!=’\n’ ; + +k)
count = k ;
for (k=0 ; k
putchar(line[k]) ;
}
در حلقة for، شمارندة k از صفر شروع ميشود و مقدار آن در هر تكرار يك واحد افزايش مييابد و در هر تكرار يك كاراكتر با تابع getchar از طريق ورودي استاندارد دريافت ميشود و به line[k] نسبت داده ميشود و وقتي كه كاراكتر خط جديد (يعني \n) وارد شد، عمل ورود كاراكترهاي رشته خاتمه مييابد كه در اين لحظه مقدار k برابر تعداد كاراكترهاي واقعي رشته خواهد بود. سپس در حلقة بعدي محتواي آراية line[ ] كه دربردارندة رشتة دريافت شده است چاپ ميگردد (دو تابع getchar و putchar دوباره بررسي خواهد شد). راه ديگر براي ورود رشتهها به حافظة كامپيوتر استفاده از تابع gets است كه در مبحث رشتهها بحث میکنیم.
براي خواندن رشتههايي كه در آنها فضاي خالي (space يا blank) وجود داشته باشد، ميتوان به طريقي از تابع scanfنيز استفاده کرد. براي اين كار ميتوان به جاي كاراكتر تبديل نوع s در رشتة كنترلي، دنبالهاي از كاراكترها را در داخل كروشه به صورت [...]قرار داد كه در اين صورت رشتة مورد نظر هريك از كاراكترهاي موجود در داخل كروشه ازجمله blank را شامل میشود.
با چنين روشي وقتي كه برنامه اجرا ميگردد، تا زماني كه كاراكترهاي متوالي خوانده شده از طريق دستگاه ورودي با يكي از كاراكترهاي موجود در درون كروشهها يكسان باشد، عمل خواندن رشتهها ادامه مييابد. فضاي خالي نيز در داخل رشتهها منظور میشود. به محض اينكه كاراكتري خوانده شود كه در داخل كروشهها وجود نداشته باشد، عمل خواندن خاتمه ميپذيرد. درضمن يك كاراكتر null به طور خودکار به پايان رشته افزوده میشود.
مثال 3ـ10 برنامة زير كاربرد تابع scanf را براي خواندن رشتههايي كه شامل حروف بزرگ و فضاي خالي است نشان ميدهد. طول اين رشته با درنظر گرفتن كاراكتر پايان رشته 80 كاراكتر خواهد بود.
کد:
#include
main ()
{
char line[80] ;
..........
scanf("%[ ABCDEFGHIJKLMNOPQRSTUVWXYZ ]", line) ;
..........
}
حال اگر از طريق ورودي، رشتة COMPUTER SCIENCE وارد شود، وقتي كه برنامه اجرا ميگردد، تمامي رشتة مزبور به آراية lineنسبت داده ميشود. به هرحال اگر يكي از حروف رشتة مزبور به حرف كوچك تايپ شود، ورود رشته در همان كاراكتر خاتمه ميپذيرد. مثلاً اگر در مثال بالا p بهصورت كوچك تايپ شود، فقط سه حرف com به آراية line نسبت داده ميشود و عمل خواندن در حرف چهارم (حرف p) خاتمه خواهد يافت.
راه ديگر آن است كه به جاي اينكه كاراكترهاي مجاز در رشتة مورد نظر را در داخل كروشه ذكر كنيم، فقط كاراكترهايي را كه مجاز نيستيم در رشتهها به كار ببريم مشخص میکنيم. براي اين كار كافي است كاراكترهاي مورد نظر را به دنبال نماد "^" كه circumflex ناميده ميشود، در داخل كروشه قرار دهيم. يعني در اينجا نقش كاراكترهاي كروشهاي عكس حالت قبلي است و وجود هركدام از آنها در داخل يك رشته موجب قطع ورود بقية كاراكترهاي رشته ميگردد و عمل خواندن رشته خاتمه ميپذيرد.
اگر كاراكتر داخل كروشهها كه بعد از "^" ميآيد، فقط كاراكتر خط جديد "\n" باشد، رشتهاي كه از طريق دستگاه ورودي استاندارد وارد ميشود هر كاراكتر اسكي به جز كاراكتر خط جديد را شامل میشود. بنابراين، كاربر ميتواند هرچه خواست بهعنوان كاراكترهاي رشته وارد كند و در پايان كليد Enterرا فشار دهد. اين كليد كاراكتر خط جديد را صادر ميكند و درنتيجه پايان رشته را اعلام خواهد كرد.
مثال 3ـ11 فرض كنيد كه يك برنامة C شامل دستورهاي زير است.
کد:
#include
main ()
{
char line[80] ;
..........
..........
scanf("%[^\n]", line) ;
..........
..........
}
وقتي كه تابع scanfدر برنامة بالا اجرا ميگردد، رشتهاي به طول نامشخص (ولي حداكثر 79كاراكتر) از طريق دستگاه ورودي استاندارد وارد ميگردد و به line نسبت داده ميشود. هيچ گونه محدوديتي در مورد كاراكترهاي تشكيلدهندة رشته وجود نخواهد داشت، فقط بايد در يك خط بگنجد. براي مثال رشتة زير از طريق صفحهكليد وارد و به line نسبت داده میشود.
WE LEARN MATHEMATICS.
تابع getchar()
براي خواندن يك كاراكتر از دستگاه ورودي، ميتوان علاوه بر تابع scanf از تابع getcharنیز استفاده کرد. تابع مزبور كه جزء كتابخانة I/O زبان استاندارد C است، كاراكتری از دستگاه ورودي استاندارد که معمولاً صفحهكليد است ميخواند. اين تابع آرگومان ندارد و به طور متعارف در يك دستور انتساب يا جايگذاري به كار میرود و كاراكتر دريافتي از ورودي را به متغيري كه در سمت چپ دستور جايگذاري مورد نظر استاختصاص ميدهد. شکل كلي آن به صورت زير است.
character variable = getchar() ;
= getchar() ; متغير كاراكتري
كه در آن متغير كاراكتري نام متغيري از نوع كاراكتر است كه بايد از قبل توصيف شده باشد.
مثال 3ـ12 به دستورهاي زير توجه کنيد.
کد:
char ch ;
ch = getchar() ;
در عبارت اول، متغير ch از نوع كاراكتر توصيف شده است. وقتي كه اجراي برنامه به دستور دوم برسد، برنامه منتظر فشار دادن كليدي از صفحهكليد ميشود. حال كاراكتر كليد فشار داده شده، به متغير ch اختصاص مییابد. چنانچه متغير ch از نوع int معرفي گردد، کد اسكي كاراكتر مربوط به كليد فشار داده شده، به آن متغير اختصاص مييابد.
اگر هنگام خواندن كاراكتر با تابع getchar، شرايط پايان فايل پيش آيد مقدار سمبوليكي EOF به طور خودكار برگشت داده میشود (اين مقدار در داخل فايل stdio.h اختصاص مییابد. به طور متعارف مقدار 1- به EOF اختصاص داده ميشود، اگرچه ممكن است اين مقدار از کامپایلری به کامپایلر ديگر فرق كند). ظاهر شدن EOF بهاين طريق، راه سادهاي براي تشخيص پايان فايل در هنگام اجراي آن است ( در اين مورد، در مبحث فايلها بيشتر بحث خواهیم کرد. لذا به هيچ وجه نگران آن نباشيد). ميتوان تابع getchar را نيز براي خواندن رشتة چند كاراكتري به صورت حلقة تكرار به كار برد كه در هر تكرار یک كاراكتر را بخواند.
تابع putchar()
اين تابع براي شمارش يك كاراكتر روي خروجي استاندارد كه معمولاً صفحه نمايش است به كار میرود و نقش آن مشابه تابع getchar اما در جهت عكس است. طبيعي است كاراكتري كه انتقال مييابد به صورت ثابت كاراكتر يا متغيری از نوع كاراكتر که آرگومان تابع مزبور است به كار میرود. شکل كلي اين تابع به صورت زير است.
putchar (character variable) ;
; (متغير كاراكتري) putchar
البته ميتوان ثابت كاراكتري را نيز به عنوان آرگومان تابع مزبور به كار برد. اين تابع با استفاده از آرايه يک رشته را در خروجي چاپ میکند.
مثال 3ـ13برنامة زير به تدريج در هر بار يك كاراكتر ميخواند و سپس آن را در خروجي چاپ ميكند.
کد:
#include
main ()
{
char ch ;
while (1)
{
ch = getchar() ;
putchar(ch) ;
}
}
در اين برنامه ch كاراكتر اعلان شده است. هر بار كه يك كاراكتر از طريق دستگاه ورودي استاندارد خوانده ميشود، به همان طريق به خروجي انتقال مييابد. اما به لحاظ اينكه عبارت مربوط به while، يعني مقدار داخل پرانتز بعد از while، برابر "1" و هميشه غيرصفر است، براساس قوانين زبان C، اين عبارت هميشه درست يا true است. بنابراين ساختار while(1) حلقهای بينهايت است و تنها راه متوقف ساختن برنامه وقفهای است كه با کليدهاي control-c عملي خواهد شد.
راه ديگر براي نوشتن برنامة بالا به صورت زير است.
کد:
#include
main ()
{
int ch ;
while ((ch=getchar()) != EOF)
putchar(ch) ;
}
این برنامه تركيب دو عمل در يك دستور را در حلقه نشان ميدهد. گفتیم EOF در برنامة بالا علامت سمبوليك پايان فايل است. آنچه در واقعيت نشانة پايان فايل را نشان میدهد تابع سيستم است. براي اين كار اغلب عدد 1- به كار میرود، ولي سيستمهاي مختلف ممکن است مقادير متفاوتي داشته باشند. با گنجانيدن فايل stdio.h و به كار بردن ثابت سمبوليكي EOF، برنامه را قابل حمل يا قابل اجرا ساختهايم. يعني، فايل مبنا روي سيستمهاي مختلف بدون تغيير اجرا میشود.
ملاحظه ميكنيد كه در روش اخير، متغير ch به جاي char به صورت int معرفي شده است. هرچه براي نشان دادن پايان فايل به كار میرود، نميتواند مقداري باشد كه يك كاراكتر را معرفي نمايد. حال چون C بهصورت int معرفي شده است، ميتواند مقادير تمام كاراكترهاي ممكن و همين طور مقدار ويژة EOF را نگهداري کند.
همان طور كه گفتیم، هر دو تابع getchar و putchar در فايل stdio.h تعريف شدهاند و ممكن است در بعضي سيستمها در فايلهاي ديگري نيز مانند فايل conio.h تعريف شده باشند.
مثال 3ـ14 برنامة زير يك خط متن را از ورودي با حروف كوچك دريافت و آن را به حروف بزرگ تبديل ميكند.
کد:
#include
main ()
{
char line[80] ;
int count , k ;
/* read in the line */
for (k=0 ; (line[k]=getchar())!=’\n’ ; + + k) ;
count = k ;
/* write out the line in upper-case */
for(k=0 ; k
putchar(toupper(line[k])) ;
}
در برنامة بالا از حلقة for و تابع کتابخانهاي toupper استفاده شده است. نقش اين تابع آن است كه حروف كوچك را به بزرگ تبديل ميكند. بنابراين اگر حروف ورودي هنگام تايپ حروف بزرگ يا ارقام و مشابه آن باشند، به شکل اولية خود نمايش داده خواهند شد. براي مثال اگر ورودي به صورت Advanced programming باشد، خروجي به صورت ADVANCED PROGRAMMING خواهد بود.
مثال 3ـ15 برنامة زير يك خط از متن را ميخواند و در آن هر كاراكتر را (به غير از كاراكتر فضاي خالي يا space) به كاراكتر بعدي تبديل ميكند و نمايش ميدهد (درواقع متن را به شکلی به صورت رمز در ميآورد و نمايش ميدهد).
کد:
#include
#define space ’ ’
main ()
{
char ch ;
ch = getchar () ; /* read a character from i/o */
while(ch!=’\n’) /*while not end of line */
{
if (ch= =space) /* leave the space */
putchar(ch) ; /* character unchanged */
else
putchar(ch+1) ; /* change other characters */
ch = getchar() ; /* get next character */
}
}
برای مثال اگر computer science2 ورودي باشد، خروجي dpnqvufs tdjfodf3 خواهد بود.
با تركيب دو دستور خواندن و تست كردن پايان متن در يك عبارت، برنامة مزبور را ميتوان به صورت ساده و فشردهتر زير نوشت.
کد:
#include
#define space ’ ’
main ()
{
char ch ;
while ((ch=getchar()) != ’\n’)
{
if (ch = = space) /* leave the space */
putchar(ch) ; /* character unchanged */
else
putchar(ch+1) ; /* change other characters */
}
}
که در اين برنامه دستور while ((ch=getchar()) != ’\n’) تركيب دو عمل در يك دستور را نشان ميدهد كه روشی متداول در زبان C است. اين دو عمل آن است كه اول به كمك تابع getchar مقداري به ch نسبت داده ميشود و سپس مقدار ch با كاراكتر خط جديد مقايسه میشود. وجود پرانتز دور عبارت ch = getchar()آن را اپراند چپ اپراتور != ميسازد. اگر آن را حذف كنيم نتيجة مطلوب به دست نميآيد زيرا اپراتور !=نسبت به اپراتور = تقدم بالاتري دارد. بنابراين اگر دستور مزبور را به صورت while (ch = getchar()!= ’\n’)بنويسيم، اول عبارت ’\n’ getchar()!=ارزيابي ميشود كه عبارتی رابطهاي است (بنابراين كاراكتر خط جديد برنميگرداند، بلكه يك مقدار برميگرداند) كه ارزش آن يك يا صفر (درست يا نادرست يعني trueيا false) خواهد بود. سپس اين مقدار به ch نسبت داده ميشود كه هدف مورد نظر ما را از دستور مزبور تأمين نميكند.
مثال 3ـ16 برنامة زير كاراكترها را از طريق ورودي صفحهكليد دريافت ميكند و آنها را به صفحة نمايش ميفرستد. اين برنامه با دريافت كاراكتر $ از ورودي خاتمه ميپذيرد.
کد:
#include
main ()
{
char ch ;
while ((ch=getchar()) != ’$’)
putchar(ch) ;
}
در اين برنامه نيز از ترکيب دو دستور در يک دستور در درون حلقة while استفاده شده است.
در زبان C علاوه بر تابع getchar دو تابع getchو getche نيز براي خواندن يك كاراكتر از ورودي به كار ميرودکه در ادامه بررسي میکنیم.
تابع getche()
اگر بخواهيم كاراكتري به كمك تابع scanf يا تابع getchar خوانده شود، بايد پس از تايپ كاراكتر مورد نظر، كليد Enter را نيز استفاده کنیم. يعني، درواقع دو تابع مزبور تا موقعي كه كليد برگشت (كه به آن carriage return يا به اختصار CR گویند) فشرده نشود ورودي را در بافر نگه ميدارند. پس از زدن كليد برگشت، دادة تايپ شده در اختيار برنامه قرار ميگيرد. حسن اين روش آن است كه اگر كليدي را اشتباه وارد كرده باشيم، ميتوانيم آن را با backspace تصحيح كنيم. يعني، قبلي را پاك كنيم و دوباره كاراكتر صحيح مورد نظر را تايپ كنيم. عيب اين كار آن است كه اين عمل در محيط محاورهاي امروز وقتگير و دردسرزاست. ازاين رو تابع getche بهوجود آمد كه در آن ديگر نيازي به تحرير كليد برگشت يا CR نيست. اشكال اين تابع آن است كه اگر كاراكتر اشتباه تحرير شود امكان تصحيح وجود ندارد. همچنين كاراكتر تحرير شده، روي صفحة تصوير نمايش داده ميشود كهاين عمل echoing ناميده ميشود. در واقع حرف e در آخر نام تابع getche به مفهوم echo (عكسالعمل) است.
تابع getch()
اين تابع همانند تابع getche است با اين تفاوت كه كاراكتر تحرير شده در صفحة تصوير ظاهر نميگردد. در مورد هريك از اين سه تابع وقتي كه كنترل اجراي برنامه به اين توابع ميرسد، برنامه منتظر فشردن كليدي در صفحهكليد ميشود. اگر متغير مورد نظر از نوع كاراكتري باشد، يعني در برنامه با فرمت %c توصيف شده باشد، مقدار كاراكتري كليد فشرده شده بهاين متغير نسبت داده ميشود و درصورتي كهاين متغير از نوع عددي باشد کد اسكي كاراكتر مربوط به كليد فشرده شده در اين متغير قرار ميگيرد.
توابع puts() و gets()
اين دو تابع اين امكان را فراهم ميسازند كه بتوان رشتههايي از كاراكترها را از طريق كنسول خواند يا در خروجي نوشت (دستگاههاي ورودي و خروجي استاندارد را كنسول نامند كه در مورد ريزكامپيوترها معمولاً صفحهكليد ورودي استاندارد و مانيتور خروجي استاندارد را تشكيل ميدهند).
تابع gets() يك رشته از كاراكترها را كه از طريق صفحهكليد وارد ميشود، ميخواند و آنها را در آدرسي قرار ميدهد كه با آرگومانهاي آن تعيين شده است و اشارهگری كاراكتری است. كاراكترهاي رشتة مورد نظر را تايپ ميكنيد و در پايان، كليد Enter را میزنید. با اين عمل به طور خودكار كاراكتر null يا ’\0’ نيز در پايان رشته قرار میگیرد. در اينجا اگر كاراكتری اشتباه تايپ شود، ميتوان آن را قبل از فشردن كليد Enter با استفاده از كليد backspace تصحيح كرد. در واقع در اينجا نيز كاراكترهاي تايپ شده در بافر ميماند و تا موقعي كه كليد برگشت فشرده نشده است در اختيار برنامه قرار نميگيرد.
تابع puts() آرگومانهاي رشتهاي خود را به صفحه نمايش ميفرستند و سپس قلم نوشتار به خط جديد انتقال مييابد.
مثال 3ـ17 برنامة زير رشتهاي را از طريق صفحهكليد ميخواند و در آراية line قرار ميدهد. سپس آن را روي خروجي نمايش ميدهد.
فراخواني تابع puts در مقايسه با فراخواني printfداراي overhead كمتري است و درنتيجه سريعتر از آن عمل ميكند زيرا تابع puts فقط يك رشته از كاراكتر را به خروجي میفرستد و نميتواند مشابه printf تبديل فرمت انجام دهد. همچنين نميتواند مقادير عددي را به عنوان خروجي داشته باشد. بنابراين چون puts فضاي كمتري ميگيرد و سريعتر از printf اجرا ميگردد، هنگامي كه در برنامهسازي حالت خيلي بهينه مورد نظر باشد، از اين تابع استفاده ميشود.
خودآزمایی 3
1. برنامهاي بنويسيد كه با استفاده از تابع printf و تابع puts رشتة زير را در دو خط جداگانه چاپ کند.
"Payam noor university"
2. برنامهاي بنويسيد كه كاراكتري از ورودي بخواند و کاراکتر بعدي آن را در خروجي چاپ كند.
3. برنامهاي بنويسيد كه عدد صحيح m1 وعدد اعشاري m2، اطلاعات كاراكتري و آدرس متغير m3 را در خروجي چاپ كند.
4. خروجي برنامة زير چيست ؟
کد:
# include < stdio. h>
main ()
{
double x ;
x = 2e + 004 ;
printf ("\n x1 = %e" , x) ;
printf ("\n x2 = %E" , x) ;
printf ("\n x3 = %g" , x) ;
}
5. خروجي برنامة زير چيست ؟
6. برنامهاي بنويسيد كه سه عدد صحيح را از ورودي بخواند و ميانگين آنها را چاپ کند.
7. برنامهاي بنويسيدكه دو متغير صحيح را از ورودي بخواند و محتويات آنها را بدون استفاده از متغير كمكي عوض کند و نتيجه را در خروجي نمايش دهد.
8. برنامهاي بنويسيد كه سن شما را برحسب روز از ورودي بخواند و مشخص كند كه چند سال، چند ماه، چند هفته و چند روز دارید.