اذهب إلى المحتوى

كيفية قراءة البرامج لمدخلات المستخدم


أسامة دمراني

تعاملت البرامج التي كتبناها في المقالات السابقة مع بيانات ثابتة، نستطيع فحصها -عند الحاجة- قبل أن يستخدمها البرنامج، ونصمم البرنامج ليناسب تلك البيانات، لكن الواقع يقول أن أغلب البرامج تتصرف وفقًا لمدخلات المستخدم، حيث يخبر المستخدم البرنامج بالملف التي يجب عليه فتحه أو تعديله مثلًا، وقد يطلب البرنامج من المستخدم بيانات ضروريةً، ويشار إلى مثل هذا المنظور البرمجي باسم واجهة المستخدم، وتصميم وبناء هذه الواجهة في السوق البرمجي وظيفة متخصصين في التفاعل بين الآلة والبشر، وفي كيفية تصميم بيئات العمل؛ أما المبرمج العادي فليس لديه تلك الرفاهية، فهو يتصرف بما لديه من منطق، ويفكر مليًا في كيفية تفاعل المستخدمين مع برنامجه أثناء استخدامه. وأبسط ميزة لواجهة المستخدم هي عرض المخرجات، وقد شرحنا ذلك من قبل بطرق بدائية وبسيطة، باستخدام الدالة 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 بدلًا من كتابته على الشاشة، ونستطيع رؤية الخرج لاحقًا باستخدام محرر نصي، لاحظ أن محث علامة الدولار $ هو المحث القياسي لمستخدمي لينكس وماك.

اقتباس

يمكن إعادة توجيه خرج الدالة print باستخدام الوسيط الاختياري ‎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.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...