تعاملت البرامج التي كتبناها في المقالات السابقة مع بيانات ثابتة، نستطيع فحصها -عند الحاجة- قبل أن يستخدمها البرنامج، ونصمم البرنامج ليناسب تلك البيانات، لكن الواقع يقول أن أغلب البرامج تتصرف وفقًا لمدخلات المستخدم، حيث يخبر المستخدم البرنامج بالملف التي يجب عليه فتحه أو تعديله مثلًا، وقد يطلب البرنامج من المستخدم بيانات ضروريةً، ويشار إلى مثل هذا المنظور البرمجي باسم واجهة المستخدم، وتصميم وبناء هذه الواجهة في السوق البرمجي وظيفة متخصصين في التفاعل بين الآلة والبشر، وفي كيفية تصميم بيئات العمل؛ أما المبرمج العادي فليس لديه تلك الرفاهية، فهو يتصرف بما لديه من منطق، ويفكر مليًا في كيفية تفاعل المستخدمين مع برنامجه أثناء استخدامه. وأبسط ميزة لواجهة المستخدم هي عرض المخرجات، وقد شرحنا ذلك من قبل بطرق بدائية وبسيطة، باستخدام الدالة print
في بايثون، والدالة write()
في جافاسكربت، وصندوق MsgBox
الحواري في VBScript.
تتمثل الخطوة التالية في تصميم واجهة المستخدم في أخذ المدخلات من المستخدم مباشرةً، وأبسط طريقة لفعل ذلك هي جعل البرنامج يطلب الدخل في وقت التشغيل، أو جعل المستخدم يمرر البيانات عندما يشغِّل البرنامج، أو -في الحالة المتقدمة- تكون لدينا واجهة رسومية فيها صناديق إدخال للنصوص مثلًا.
سنشرح في هذا المقال الطريقتين الأولى والثانية فقط، ونترك الواجهة الرسومية لوقت لاحق من هذه السلسلة لأنها أكثر تعقيدًا، غير أن هناك وحدةً تسمح لنا بتنفيذ صناديق حوارية رسومية بسيطة، وسندرِجها في هذا المقال أيضًا.
سنرى الآن كيف نستطيع الحصول على البيانات من المستخدم في جلسة بايثون تفاعلية عادية تعمل في IDLE، أو في طرفية نظام التشغيل، ثم نحاول الحصول على نفس البيانات داخل برنامج.
دخل المستخدم في بايثون
نحصل على ما يدخله المستخدم في بايثون بالشكل التالي:
>>>> print( input("Type something: ") )
تعرض input()
المحث المعطى، وهو "Type something" في حالتنا، وتلتقط أي شيء يكتبه المستخدم، ثم تعرض print()
تلك الإجابة، ونستطيع إسناد ما يدخله المستخدم إلى متغير:
>>> resp = input("What's your name? ")
يمكننا طباعة أي قيمة يلتقطها ذلك المتغير:
>>> print( "Hi " + resp + ", nice to meet you" )
لاحظ أننا لم نستخدم هذه المرة عامل تنسيق السلسلة النصية لعرض القيمة المخزَّنة في المتغير resp
، واكتفينا بإدراج القيمة بين سلسلتين نصيتين، ودمجنا السلاسل الثلاث باستخدام عامل إضافة السلاسل النصية +
، وقيمة المتغير resp
هي التي التُقطت من المستخدم بواسطة input()
.
لاحظ كيف استخدمنا المسافات داخل السلاسل النصية التي في المحث المعطى إلى input()
وسلسلة الخرج، وانظر الجزء الثالث من سلسلة الخرج الذي يبدأ بالفاصلة المتبوعة بمسافة، إذ يخطئ الكثيرون في أماكن تلك المسافات عند إنتاج خرج كهذا، لذا تفحص مثل هذه المواضع جيدًا عند اختبارك للبرامج.
قرأ المثال السابق السلاسل النصية، لكن ماذا عن أنواع البيانات الأخرى؟ نجيب على هذا السؤال بأن بايثون تأتي بمجموعة كاملة من دوال تحويل البيانات، التي تستطيع تحويل سلسلة نصية إلى نوع بيانات آخر، مع وجوب أن تكون البيانات التي في السلسلة النصية متوافقةً مع ذلك النوع وإلا فسنحصل على خطأ، ولنعدل مثلًا جدول الضرب الذي ذكرناه من قبل، ليقرأ قيمة المضاعف من المستخدم:
>>> multiplier = input("Which multiplier do you want? Pick a number ") >>> multiplier = int(multiplier) >>> for j in range(1,13): ... print( "%d x %d = %d" % (j, multiplier, j * multiplier) )
نقرأ في هذا المثال القيمة من المستخدم، ثم نحولها إلى عدد صحيح باستخدام دالة التحويل int()
، كما يمكننا تحويلها إلى عدد ذي فاصلة عائمة باستخدام float()
عند الحاجة إلى ذلك، ونفذنا التحويل هنا في سطر منفصل لنفصّل الخطوات لتبسيط الشرح، أما في البرمجة في سوق العمل من الشائع تغليف استدعاء input()
داخل التحويل كما يلي:
>>> multiplier = int( input("Which multiplier do you want? Pick a number ") ) >>> for j in range(1,13): ... print( "%d x %d = %d" % (j, multiplier, j * multiplier) )
غلفنا استدعاء input()
داخل استدعاء int()
، لنجرب تطبيق هذا في برنامج حقيقي، وسنستخدم برنامج دليل جهات الاتصال الذي أنشأناه باستخدام قاموس في مقال البيانات وأنواعها بما أننا نستطيع كتابة الحلقات التكرارية وقراءة بيانات الإدخال:
# أنشئ قاموس فارغًا لدليل جهات اتصال addressBook = {} # اقرأ المدخلات إلى أن تصل إلى سلسلة فارغة print() # print a blank line name = input("Type the Name(leave blank to finish): ") while name != "": entry = input("Type the Street, Town, Phone.(Leave blank to finish): ") addressBook[name] = entry name = input("Type the Name(leave blank to finish): ") # والآن، اطلب عرض واحد منها name = input("Which name to display?(leave blank to finish): ") while name != "": print( name, addressBook[name] ) name = input("Which name to display?(leave blank to finish): ")
لعل هذا البرنامج هو أكبر برنامج كتبناه حتى الآن، إذ يشمل تسلسلات وحلقتين تكراريتين، وعلى الرغم من أن تصميم واجهة المستخدم فيه ليس مثاليًا؛ إلا أنه يؤدي الغرض المطلوب منه، وسنرى كيفية تطوير هذه الواجهة في مقالات تالية؛ أما الآن فنريد الإشارة إلى استخدام الاختبار البولياني في حلقات while
لتحديد رغبة المستخدم في التوقف، لاحظ أيضًا أننا مع استخدامنا لقائمة في مقال مدخل إلى البيانات وأنواعها: أنواع البيانات الأساسية لتخزين البيانات في حقول منفصلة، إلا أننا خزنّاها هنا في سلسلة نصية واحدة، لأننا لم نشرح كيفية تقسيم السلسلة النصية إلى حقول منفصلة بعد.
الإدخال في لغة VBScript
تقرأ تعليمات InputBox في لغة VBScript دخل المستخدم كما يلي:
<script type="text/vbscript"> Dim Input Input = InputBox("Enter your name") MsgBox ("You entered: " & Input) </script>
تمثل دالة InputBox
صندوقًا حواريًا مع محث وحقل إدخال، وتعيد محتويات حقل الإدخال، وهناك عدة قيم يمكن تمريرها إليها، مثل سلسلة عنوان الصندوق الحواري، إضافةً إلى المحث، فإذا ضغط المستخدم زر الإلغاء Cancel، فستعيد الدالة سلسلةً فارغةً بغض النظر عن محتويات حقل الإدخال.
سيبدو مثال دليل الاستخدام في لغة VBScript كما يلي:
<script type="text/vbscript"> Dim dict,name,entry ' Create some variables. Set dict = CreateObject("Scripting.Dictionary") name = InputBox("Enter a name", "Address Book Entry") Do While name <> "" entry = InputBox("Enter Details - Street, Town, Phone number", "Address Book Entry") dict.Add name, entry ' Add key and details. name = InputBox("Enter a name","Address Book Entry") Loop ' Now read back the values name = InputBox("Enter a name","Address Book Lookup") Do While name <> "" MsgBox(name & " - " & dict.Item(name)) name = InputBox("Enter a name","Address Book Lookup") Loop </script>
لاحظ أن الهيكل الأساسي هنا مطابق تمامًا لبرنامج بايثون، مع أن بعض الأسطر أطول هنا بسبب الحاجة إلى التصريح المسبق عن المتغيرات باستخدام Dim
في VBScript، وبسبب الحاجة إلى تعليمة Loop
لإنهاء كل حلقة.
قراءة المدخلات في جافاسكربت
تمثل جافاسكربت تحديًا في هذا المجال لأنها تعمل أساسًا داخل المتصفحات، ولقراءة المدخلات يمكننا استخدام صندوق إدخال بسيط كما في VBScript باستخدام دالة prompt()
، أو قراءة المدخلات من عنصر استمارة في HTML، أو استخدام تقنية Active Scripting الخاصة بمايكروسوفت -وذلك في إنترنت إكسبلورر فقط- لتوليد صندوق InputBox
كما في VBSCript، ولتنويع الأمثلة سنشرح كيفية استخدام تقنية عنصر الاستمارة في HTML، فإذا لم تكن تعرضت للغة HTML من قبل فانظر توثيقها في موسوعة حسوب، أو انسخ ما سنكتبه هنا إذا شئت.
<form id='entry' name='entry'> <p>Type a value then click outside the field with your mouse</p> <input type='text' name='data' onChange='alert("We got a value of " + document.forms["entry"].data.value);'> </form>
تتكون شيفرة HTML أعلاه من عنصر استمارة form
الذي يحتوي على فقرة <p>
من سطر واحد، حيث يمثل رسالةً إلى المستخدم، وحقل إدخال نصي input
، يحتوي على شيفرة من سطر واحد مرتبطة به، تنفَّذ في كل مرة تتغير فيها القيمة المدخلة، ووظيفة هذه الشيفرة ببساطة، أن تخرِج صندوق رسالة alert
يشبه ذاك الذي في VBScript، ويحتوي على القيمة التي في الحقل النصي.
كما نرى في أول سطر في الشيفرة، فإن للاستمارة خاصيتين هما id
و name
، وتحتويان على القيمة entry
، وتُخزَّن الاستمارات في سياق المستند document
داخل مصفوفة مفهرسة بالاسم، لأنه يمكن استخدام مصفوفات جافاسكربت مثل القواميس كما ذكرنا من قبل، ويحتوي حقل input
على الخاصية name
التي تحمل القيمة data
داخل سياق الاستمارة، ومع ذلك نستطيع الإشارة إلى قيمة value
حقل ما داخل برنامج جافاسكربت كما يلي:
document.forms["entry"].data.value
لن نشرح هنا مثال دليل جهات الاتصال في جافاسكربت، لأن HTML ستتعقَّد وسنحتاج إلى شرح الدوال، لذا سنؤجل هذا إلى مقال تال.
مجاري الدخل والخرج القياسية
ننظر الآن في أحد المفاهيم الأساسية في حوسبة سطر الأوامر، وهو مفهوم مجاري البيانات data streams، فمصطلح stdin
هو أحد المصطلاحات الحاسوبية التي تشير إلى جهاز الدخل القياسي standard input device، وهو عادةً لوحة المفاتيح، وبالمثل يشير stdout
إلى جهاز الخرج القياسي، وهو الشاشة عادةً، وسنرى إشارات كثيرةً إلى هذين المصطلحين عند الحديث عن البرمجة، كما يوجد مصطلح ثالث لا يُستخدم كثيرًا، وهو stderr
الذي يشير إلى المكان الذي ترسَل إليه جميع أخطاء الطرفية، ويظهر عادةً في نفس مكان stdout، ويطلق على هذه المصطلحات اسم مجاري البيانات، لأن البيانات تظهر في صورة مجارٍ من البايتات التي تتدفق إلى الأجهزة، وقد أُعدَّ كل من stdin وstdout ليشبها الملفات، ليتوافقا مع شيفرة معالجة الملفات.
وتوجد هذه المصطلحات في لغة بايثون داخل الوحدة sys
، وتسميان sys.stdin
وsys.stdout
، وتستخدم الدالة input()
تلقائيًا stdin، بينما تستخدم print()
الخرج القياسي stdout.
نستطيع أن نقرأ من الدخل القياسي stdin، وأن نكتب مباشرةً في الخرج القياسي stdout، مما يسمح لنا بتحكم دقيق في الدخل والخرج، لننظر في المثال التالي إلى القراءة من الدخل القياسي:
import sys print( "Type a value: ", end='') # تمنع السطر الجديد value = sys.stdin.readline() # صراحة stdin استخدم print( value )
تطابق الشيفرة أعلاه تقريبًا الشيفرة التالية:
print( input("Type a value: ") )
إن ميزة النسخة الصريحة هنا أننا نستطيع جعل الدخل القياسي يشير إلى ملف حقيقي، ليقرأ البرنامج الدخل الخاص به من الملف عوضًا عن الطرفية، وهذه الطريقة مفيدة في حالة جلسات الاختبار الطويلة التي يقرأ البرنامج فيها مدخلاته من ملف بدلًا من الجلوس وكتابة الدخل يدويًا كلما طلبه البرنامج، مما يضمن تكرار تشغيل الاختبار مع ثبات الدخل في كل مرة، وكذلك الخرج، وتسمى هذه التقنية -التي تكرر الاختبارات السابقة لضمان عدم تعطل شيء في الشيفرة- باسم الاختبار الارتدادي regression testing.
وأخيرًا لدينا مثال عن خرج مباشر إلى sys.stdout
، ويمكن إعادة توجيهه إلى ملف كذلك، وتكافئ الدالة print
ما يلي:
sys.stdout.write("Hello world\n") # \n= newline
لا شك أننا نستطيع تحقيق نفس النتيجة باستخدام سلاسل التنسيق النصية، إذا كنا نعرف الشكل الذي ستكون البيانات عليه، لكن إذا لم نعرف شكلها إلا وقت التشغيل، فمن الأسهل أن نرسلها إلى الخرج القياسي، بدلًا من محاولة بناء سلسلة تنسيق معقدة وقت التشغيل.
إعادة توجيه الدخل والخرج القياسيين
يمكن توجيه الدخل والخرج القياسيين إلى ملفات من داخل برنامجنا مباشرةً، باستخدام تقنيات بايثون المعتادة للتعامل مع الملفات، والتي سنشرحها فيما يلي، لكن الطريقة الأسهل هي من خلال نظام التشغيل، تتصرف أوامر النظام عند استخدام إعادة التوجيه في سطر الأوامر كما يلي:
C:> dir C:> dir > dir.txt
يطبع الأمر الأول قائمةً من المجلدات على الشاشة -والتي تمثل الخرج القياسي-، بينما يطبع الأمر الثاني تلك القائمة إلى ملف، فقد أخبرنا البرنامج أن يوجه الخرج القياسي إلى الملف dir.txt
، ويمكن فعل نفس الشيء مع برنامج بايثون كما يلي:
$ python myprogram.py > result.txt
ستشغّل الشيفرة السابقة البرنامج myprogram.py
، وستكتب الخرج إلى الملف result.txt
بدلًا من كتابته على الشاشة، ونستطيع رؤية الخرج لاحقًا باستخدام محرر نصي، لاحظ أن محث علامة الدولار $
هو المحث القياسي لمستخدمي لينكس وماك.
اقتباسيمكن إعادة توجيه خرج الدالة
file="result.txt"
، وتتيح هذه الطريقة التحكم في كل تعليمة، لكن يصعب كتابتها في البرنامج، وهي مناسبة لطباعة مجموعة فرعية بعينها من خرج البرنامج، ولا يفضل استخدامها في حفظ الخرج إلى ملف، وحتى في حالة المجموعة الفرعية يفضَّل توجيه الخرج إلى ملف، لذا لا يُستخدم خيارfile=
كثيرًا.
نستخدم علامة <
بدلًا من >
لتوجيه الدخل القياسي إلى ملف، ننشئ في المثال التالي ملفًا باسم echoinput.py
يحتوي على الشيفرة:
import sys inp = input() while inp != '': print( inp ) inp = input()
نستطيع الآن تشغيل الملف من سطر الأوامر كما يلي:
$ python echoinput.py
يجب أن تكون النتيجة برنامجًا يعيد طباعة أي شيء تكتبه إلى أن تكتب سطرًا فارغًا، والآن أنشئ ملفًا نصيًا بسيطًا باسم input.txt
يحتوي بعض الأسطر النصية، وشغّل البرنامج الأخير مرةً أخرى معيدًا توجيه الدخل من input.txt
:
$ python echoinput.py < input.txt
ستعيد بايثون طباعة الأسطر النصية الموجودة في الملف.
نستطيع اختبار عدة سيناريوهات على برامجنا بسهولة إذا استخدمنا تلك التقنية على عدة ملفات، مثل قيم البيانات المعطوبة أو أنواع البيانات الخاطئة، وننفذ ذلك بأسلوب موثوق قابل للتكرار، كما نستطيع استخدامها لمعالجة البيانات كبيرة الحجم من ملف، مع السماح بالإدخال اليدوي للبيانات صغيرة الحجم باستخدام نفس البرنامج، وهكذا نرى أن الدخل والخرج القياسيين مفيدان جدًا للمبرمجين.
معاملات سطر الأوامر
لدينا نوع آخر من الإدخال، وهو الإدخال من سطر الأوامر، كما في حالة تشغيل المحرر النصي من سطر أوامر النظام:
$ edit Foo.txt
يستدعي نظام التشغيل هنا البرنامج الذي يحمل الاسم edit
، ليمرر إليه اسم الملف الذي نريد تعديله، وهو Foo.txt
هنا. لكن كيف يقرأ المحرر اسم الملف؟، يوفر نظام التشغيل في أغلب لغات البرمجة مصفوفةً أو قائمةً من سلاسل نصية تحتوي كلمات سطر الأوامر، وبناءً عليه سيحتوي العنصر الأول على الأمر نفسه، ثم يحتوي العنصر التالي على الوسيط الأول وهكذا، وقد توجد بعض المتغيرات السحرية هنا والتي يُطلق عليها argc، وهي اختصار argument count، وتحمل عدد العناصر الموجودة في القائمة، وتحتفظ الوحدة sys
في بايثون بهذه القائمة وتسميها argv
، وهي اختصار argument values، وأول عنصر فيها argv[0]
هو اسم ملف السكربت الذي ينفَّذ، ولا تحتاج بايثون قيمةً من النوع argc
لأننا نستطيع استخدام التابع len()
لإيجاد طول السلسلة، بل لا نحتاج إلى ذلك أصلًا في أغلب الحالات، لأننا نمر على القائمة باستخدام حلقة for
الخاصة ببايثون:
import sys for item in sys.argv: print( item ) print( "The first argument was:", sys.argv[1] )
لاحظ أن هذه الشيفرة لا تعمل إلا إذا وُضعت في ملف مثل args.py
، ونُفذت من محث نظام التشغيل كما يلي:
C:\PYTHON\PROJECTS> python args.py 1 23 fred args.py 1 23 fred The first argument was: 1 C:\PYTHON\PROJECTS>
يجب إحاطة اسم الوسيط بعلامات اقتباس إذا احتوى على مسافات، كما يلي:
C:\PYTHON\PROJECTS> python args.py "Alan Gauld" fred args.py Alan Gauld fred The first argument was: Alan Gauld C:\PYTHON\PROJECTS>
جافاسكربت وVBscript
لا نرى مفهوم وسائط سطر الأوامر في هاتين اللغتين لأنهما موجهتان للعمل داخل المتصفحات، فإذا استخدمناهما داخل بيئة Windows Script Host الخاصة بمايكروسوفت، فستوفر بيئة WSH آليةً لاستخراج مثل تلك الوسائط من كائن WshArguments
الذي تملؤه WSH في وقت التشغيل.
خاتمة
لن نذهب أبعد من هذا الحد في شأن مدخلات المستخدم في هذه السلسلة، وهو الحد الذي يمكننا من كتابة برامج مفيدة، وقد كان ذلك كل ما يستطيعه المبرمج في الأيام الأولى لأنظمة يونكس أو لمبرمجي الحواسيب الشخصية، ولا شك أن البرامج الرسومية تقرأ المدخلات من المستخدم، لكن التقنيات التي تستخدمها مختلفة كليًا، لهذا سنؤجلها إلى مقالات لاحقة من هذه السلسلة.
تعلمنا في هذا المقال استخدام الدالة input()
لقراءة السلاسل النصية، وعرفنا أنها تعرض سلسلةً تطلب إدخالًا من المستخدم، كما تعرفنا إلى الدخل والخرج القياسيين لمجاري البيانات، وكيفية إعادة توجيههما ليكونا في صورة ملفات، وعرفنا أن input()
تعمل مع stdin، وأن print()
تعمل مع stdout.
ويمكن الحصول على وسطاء سطر الأوامر من قائمة argv
المستوردة من وحدة sys
في بايثون، حيث يكون العنصر الأول هو اسم البرنامج، ورأينا أن جافاسكربت وVBScript تستطيعان قراءة المدخلات من استمارات الويب forms
أو من خلال الصناديق الحوارية، لكن ليس لهما وصول إلى stdin
، كما تستطيعان عرض الخرج بكتابته إلى المستند document
أو من خلال الصناديق الحوارية، وليس لهما وصول إلى stdout
.
ترجمة -بتصرف للفصل التاسع: Conversing with the user من كتاب Learning To Program لصاحبه Alan Gauld.
اقرأ أيضًا
- المقال التالي: مقدمة في البرمجة الشرطية
- المقال السابق: أسلوب كتابة الشيفرات البرمجية وتحقيق سهولة قراءتها
- دليلك الشامل لتعلم البرمجة
- ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت
- النسخة الكاملة لكتاب تعلم البرمجة للمبتدئين
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.