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

معالجة النصوص باستخدام لغة بايثون Python


عبد اللطيف ايمش

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

كل ما سبق ستتعلمه في هذا المقال وسنزيد على ذلك؛ وسنرى مشروعين عمليين: حافظة بسيطة التي تخزن عدة سلاسل نصية من النصوص، وبرنامج لأتمتة العملية المملة لتنسيق النصوص يدويًا.

التعامل مع السلاسل النصية

لنلقِ نظرةً على الطرائق التي تسمح لنا فيها بايثون بالكتابة والطباعة والوصول إلى السلاسل النصية في برامجنا.

السلاسل النصية المجردة

كتابة القيم النصية في شيفرة بايثون هو أمر بالغ السهولة: نبدأ وننتهي بعلامة اقتباس مفردة.

لكن ماذا سيحدث لو أردنا استخدام علامة اقتباس داخل السلسلة النصية؟ إذا كتبنا ‎'That is Alice's cat.'‎ فسيحدث خطأ لأن بايثون ستظن أن السلسلة النصية قد انتهت بعد Alice وبقية السطر s cat.' هي شيفرة غير صالحة في بايثون.

لحسن الحظ هنالك عدة طرائق لكتابة السلاسل النصية.

علامات الاقتباس المزدوجة

يمكن أن تبدأ السلاسل النصية وتنتهي عبر كتابة علامتي اقتباس مزدوجتين، وهي تعمل كما في علامات الاقتباس المفردة. إحدى فوائد استخدام علامات الاقتباس المزدوجة هي إمكانية كتابة علامة الاقتباس الفردية داخلها:

>>> spam = "That is Alice's cat."

ولأن السلسلة النصية تبدأ بعلامة اقتباس مزدوجة، فستعلم بايثون أن علامة الاقتباس المفردة هي جزء من السلسلة النصية ولا تمثل نهايتها. لكن ماذا لو احتجنا إلى استخدام علامات الاقتباس المفردة والمزدوجة معًا في السلسلة النصية؟ سنحتاج هنا إلى محارف التهريب.

محارف التهريب

تسمح لنا محارف التهريب escape characters باستخدام محارف ليس من الممكن إدراجها مباشرةً في السلسلة النصية.

يتألف محرف التهريب من خط مائل خلفي backslash \ متبوعًا بأحد المحارف التي نريد إضافتها إلى السلسلة النصية؛ وصحيحٌ أن اسمه هو «محرف» التهريب، لكن يتألف من محرفين ويشار إليه عادةً بصيغة المفرد escape character.

فمثلًا محرف التهريب لعلامة الاقتباس المفردة هو ‎\' وذب الشيفرة الآتية:

>>> spam = 'Say hi to Bob\'s brother.'

ولأننا وضعنا خط مائل خلفي قبل علامة الاقتباس المفردة في Bob\'s فستعلم بايثون أننا لا نقصد أن ننهي السلسلة النصية. تسمح لنا محارف التهريب\' و \" بوضع علامات الاقتباس داخل السلاسل النصية المحاطة بعلامات اقتباس مفردة ومزدوجة على الترتيب. الجدول 6-1 يوضح بعض محارف التهريب التي تستطيع استخدامها.

فيما يلي محارف التهريب:

  • ‎\'‎: علامة اقتباس مفردة
  • ‎\": علامة اقتباس مزدوجة
  • ‎\t: علامة الجدولة tab
  • ‎\n: سطر جديد newline
  • \\: خط مائل خلفي

جرب السلسلة النصية الآتية:

>>> print("Hello there!\nHow are you?\nI\'m doing fine.")
Hello there!
How are you?
I'm doing fine.

السلاسل النصية الخام

يمكنك وضع الحرف r قبل علامة الاقتباس في بداية السلسلة النصية لجعلها سلسلة نصية خام. السلسلة النصية الخام raw string تتجاهل جميع محارف التهريب وتظهر جميع الخطوط المائلة الخلفية كما هي:

>>> print(r'That is Carol\'s cat.')
That is Carol\'s cat.

ولأنها سلسلة نصية خام فستعدّ بايثون الخط الخلفي المائل على أنه جزء من السلسلة النصية وليس جزءًا من محرف التهريب. يمكن أن تكون السلاسل النصية الخام مفيدةً إن كانت تكتب سلاسل نصية فيها عدد من الخطوط المائلة الخلفية، مثل مسارات الملفات في نظام ويندوز r'C:\Users\Al\Desktop'‎ أو التعابير النمطية regular expressions المشروحة في المقال القادم.

السلاسل النصية متعددة الأسطر بعلامات اقتباس ثلاثية

صحيحٌ أن بإمكاننا استخدام محرف التهريب ‎\n لطابعة سطر جديد داخل سلسلة نصية، لكن من الأسهل استخدام السلاسل النصية متعددة الأسطر. تبدأ السلسلة النصية متعددة الأسطر في بايثون بثلاث علامات اقتباس مفردة أو مزدوجة، وستعد جميع علامات الاقتباس ومسافات الجدولة tabs والأسطر الجديدة على أنها جزءٌ من السلسلة النصية المحاطة بعلامات اقتباس ثلاثية.

لاحظ أن قواعد تنسيق بايثون الخاصة بالمسافات البادئة في الكتل البرمجية لا تنطبق على السلاسل النصية متعددة الأسطر.

print('''Dear Alice,

Eve's cat has been arrested for catnapping, cat burglary, and extortion.

Sincerely,
Bob''')

احفظ ما سبق في ملف باسم catnapping.py وشغله:

Dear Alice,

Eve's cat has been arrested for catnapping, cat burglary, and extortion.

Sincerely,
Bob

لاحظ أن علامة الاقتباس المفردة في Eve's لا تحتاج إلى تهريب، فتهريب علامات الاقتباس المفردة والمزدوجة هو أمرٌ اختياري حين استخدام السلاسل النصية متعددة الأسطر. السلسلة النصية الموجودة في دالة print() الآتية تكافئ المثال السابق دون استخدام السلاسل النصية متعددة الأسطر:

print('Dear Alice,\n\nEve\'s cat has been arrested for catnapping, cat
burglary, and extortion.\n\nSincerely,\nBob')

التعليقات متعددة الأسطر

صحيحٌ أن رمز المربع # يرمز إلى بداية تعليق حتى نهاية السطر، لكن من الشائع استخدام سلسلة نصية متعددة الأسطر للتعليقات التي تمتد لأكثر من سطر. الشيفرات الآتية صالحة تمامًا في بايثون:

"""This is a test Python program.
Written by Al Sweigart al@inventwithpython.com

This program was designed for Python 3, not Python 2.
"""

def spam():
    """This is a multiline comment to help
    explain what the spam() function does."""
    print('Hello!')

فهرسة وتقسيم السلاسل النصية

تستعمل السلاسل النصية الفهارس ويمكن تقسيمها كما في القوائم lists. يمكنك أن تعدّ السلسلة النصية ‎'Hello, world!'‎ على أنها قائمة وكل حرف فيها يمثل عنصرًا في تلك القائمة مع فهرس مرتبط به.

'   H   e   l   l   o   ,       w   o   r   l    d    !   '
    0   1   2   3   4   5   6   7   8   9   10   11   12

لاحظ تضمين الفراغ وإشارة التعجب، وسيكون عدد الأحرف هو 13، بدءًا من الحرف H في الفهرس 0 إلى الحرف ! في الفهرس 12.

>>> spam = 'Hello, world!'
>>> spam[0]
'H'
>>> spam[4]
'o'
>>> spam[-1]
'!'
>>> spam[0:5]
'Hello'
>>> spam[:5]
'Hello'
>>> spam[7:]
'world!'

إذا حددت فهرسًا فستحصل على المحرف الموجود في ذاك الموضع في السلسلة النصية، وإذا حددت مجالًا من فهرسٍ ما إلى آخر فسيُضمَّن المحرف الموجود في فهرس البداية ولن يضمن المحرف الموجود في فهرس النهاية، ولذا إذا كان لدينا المتغير spam الذي فيه ‎'Hello, world!' فإن spam[0:5]‎ هو 'Hello'؛ فالسلسلة النصية الجزئية التي ستحصل عليها من spam[0:5]‎ ستتضمن كل محرف موجود في المجال spam[0]‎ حتى spam[4]‎ ولن تحتوي الفاصلة الموجودة في الفهرس 5 ولا الفراغ في المحرف 6.

هذا السلوك يشبه سلوك الدالة range(5)‎ التي ستؤدي -حين استعمالها مع for- إلى المرور على الأرقام حتى الرقم 5 دون تضمينه.

لاحظ أن تقسيم السلاسل النصية لا يغير السلسلة النصية الأصلية، ويمكننا حفظ القيمة الناتجة في متغير منفصل:

>>> spam = 'Hello, world!'
>>> fizz = spam[0:5]
>>> fizz
'Hello'

بتقسيم السلسلة النصية ثم تخزينها في متغير آخر فسنتمكن من الوصول إلى السلسلة النصية الأصلية والسلسلة النصية المقتطعة بسهولة.

العوامل in و not in مع السلاسل النصية

يمكن استخدام العاملين in و not in مع السلاسل النصية كما في القوائم lists. التعبير البرمجي الذي فيه سلسلتين نصيتين مجموعٌ بينها بالعامل in أو not in سينتج القيمة المنطقية True أو False:

>>> 'Hello' in 'Hello, World'
True
>>> 'Hello' in 'Hello'
True
>>> 'HELLO' in 'Hello, World'
False
>>> '' in 'spam'
True
>>> 'cats' not in 'cats and dogs'
False

تختبر التعابير البرمجية السابقة إن كانت السلسلة النصية الأولى (كما هي بحذافيرها، مع حالة الأحرف فيها) موجودةً في السلسلة النصية الثانية.

وضع السلاسل النصية داخل سلاسل نصية أخرى

من الشائع في البرمجة وضع سلسلة نصية داخل سلسلة نصية أخرى، واستعملنا حتى الآن العامل + لجمع السلاسل النصية كما يلي:

>>> name = 'Al'
>>> age = 4000
>>> 'Hello, my name is ' + name + '. I am ' + str(age) + ' years old.'
'Hello, my name is Al. I am 4000 years old.'

لكن ذلك يتطلب كتابةً كثيرة، ومن الأسهل أن ندس السلاسل النصية داخل بعضها string interpolation، وبهذه الطريقة نستعمل العامل ‎%s داخل السلاسل النصية كمؤشر لكي يستبدل مسبقًا إلى القيم التي تلي السلسلة النصية.

إحدى ميزات استخدام هذه الطريقة هو عدم حاجتنا إلى استدعاء الدالة str()‎ لتحويل القيم إلى سلاسل نصية:

>>> name = 'Al'
>>> age = 4000
>>> 'My name is %s. I am %s years old.' % (name, age)
'My name is Al. I am 4000 years old.'

أضافت بايثون 3.6 ما يسمى بالسلاسل النصية المنسقة f-strings، وهي تشبه دس السلاسل النصية لكنها تتيح إضافة التعابير البرمجية بين قوسين مجعدين مباشرةً؛ تذكر أن تضيف الحرف f قبل علامة الاقتباس الابتدائية في السلسلة النصية المنسقة:

>>> name = 'Al'
>>> age = 4000
>>> f'My name is {name}. Next year I will be {age + 1}.'
'My name is Al. Next year I will be 4001.'

إذا لم تتذكر تضمين الحرف f قبل علامة الاقتباس فستعامل الأقواس على أنها جزء من السلسلة النصية:

>>> 'My name is {name}. Next year I will be {age + 1}.'
'My name is {name}. Next year I will be {age + 1}.'

توابع مفيدة للتعامل مع السلاسل النصية

يشرح هذا القسم أكثر التوابع شيوعًا التي تحلل السلاسل النصية وتعالجها وتنتج سلاسل نصية معدلة.

التوابع upper()‎ و lower()‎ و isupper()‎ و islower()‎

يعيد التابعان upper()‎ و lower() سلسلةً نصيةً جديدةً تحوَّل فيها أحرف السلسلة النصية الأصلية إلى حالة الأحرف الكبيرة أو الصغيرة على التوالي. أما المحارف غير النصية أو غير اللاتينية فتبقى كما هي:

>>> spam = 'Hello, world!'
>>> spam = spam.upper()
>>> spam
'HELLO, WORLD!'
>>> spam = spam.lower()
>>> spam
'hello, world!'

لاحظ أن هذان التابعان لا يغيران السلسلة النصية الأصلية وإنما يعيدان سلسلةً نصيةً جديدةً. إذا أردت تعديل السلسلة النصية الأصلية فعليك استدعاء التابع upper()‎ أو lower()‎ على السلسلة النصية ثم إسناد الناتج مباشرةً إلى المتغير الذي يحتوي على السلسلة النصية الأصلية؛ أي أننا سنكتب شيئًا يشبه spam = spam.upper()‎ لتعديل السلسلة النصية المخزنة في المتغير spam بدلًا من كتابة spam.upper()‎ فقط، وهذا يشبه حالة وجود متغير اسمه eggs يحتوي القيمة 10، فكتابة التعبير البرمجي eggs + 3 لا يؤدي إلى تغيير القيمة المخزنة في المتغير eggs مباشرة، وإنما علينا كتابة eggs = eggs + 3.

التابعان upper() و lower() مفيدان إن أردنا مقارنة سلسلتين نصيتين مقارنةً غير حساسةٍ لحالة الأحرف. فمثلًا السلسلتان النصيتان 'great' و 'GREat' غير متساويتين؛ لكن قد لا يهمنا في بعض الحالات التي نطلب فيها مدخلات المستخدم إن كان قد كتب Great أم GREAT أم gREAt. انظر المثال الآتي الذي نحول فيه السلسلة النصية إلى حالة الأحرف الصغيرة:

print('How are you?')
feeling = input()
if feeling.lower() == 'great':
    print('I feel great too.')
else:
    print('I hope the rest of your day is good.')

حينما تشغل البرنامج السابق فسيظهر لك سؤال، وإذا أدخلت الكلمة great بأي شكل من الأشكال فستظهر لك العبارة ‎'I feel great too.'. من المفيد جدًا إضافة آلية للتعامل مع اختلاف حالات الأحرف في مدخلات المستخدم في برامجك، إذ سيسهل ذلك استخدامها كثيرًا.

How are you?
GREat
I feel great too.

يعيد التابعان isupper()‎ و islower()‎ قيمةً منطقية True إن كانت السلسلة النصية تحتوي على حرف واحد على الأقل وكان ذاك الحرف كبيرًا أو صغيرًا على التوالي وبالترتيب؛ وإلا فستعيد القيمة False:

>>> spam = 'Hello, world!'
>>> spam.islower()
False
>>> spam.isupper()
False
>>> 'HELLO'.isupper()
True
>>> 'abc12345'.islower()
True
>>> '12345'.islower()
False
>>> '12345'.isupper()
False

ولأن القيمة المعادة من التابعين upper()‎ و lower()‎ هي سلاسل نصية، فيمكننا استدعاء توابع التعامل مع السلاسل النصية على القيم المعادة من تلك التوابع مباشرةً، وتبدو هذه التعابير البرمجية على أنها سلسلة من التوابع وراء بعضها كما في المثال الآتي:

>>> 'Hello'.upper()
'HELLO'
>>> 'Hello'.upper().lower()
'hello'
>>> 'Hello'.upper().lower().upper()
'HELLO'
>>> 'HELLO'.lower()
'hello'
>>> 'HELLO'.lower().islower()
True

مجموعة توابع isX()‎

بالإضافة إلى التابعين islower()‎ و isupper()‎ هنالك عدد من التوابع التي يبدأ اسمها بالكلمة is، وتعيد تلك التوابع قيمةً منطقية التي تشرح طبيعة السلسلة النصية. هذه بعض تلك التوابع:

  • isalpha()‎ يعيد True إذا احتوت السلسلة النصية على أحرف عادية فقط ولم تكن فارغة.
  • isalnum()‎ يعيد True إذا احتوت السلسلة النصية على أحرف عادية وأرقام فقط ولم تكن فارغة.
  • isdecimal()‎ يعيد True إذا احتوت السلسلة النصية على أرقام فقط ولم تكن فارغة.
  • isspace()‎ يعيد True إذا احتوت السلسلة النصية على فراغات عادية ومسافات جدولة tabs وأسطر جديدة newlines فقط ولم تكن فارغة.
  • istitle()‎ يعيد True إذا بدأت السلسلة النصية بحرف كبير متبوعٌ بمجموعة من الأحرف الصغيرة.

لنجرب تلك التوابع عمليًا:

>>> 'hello'.isalpha()
True
>>> 'hello123'.isalpha()
False
>>> 'hello123'.isalnum()
True
>>> 'hello'.isalnum()
True
>>> '123'.isdecimal()
True
>>> '    '.isspace()
True
>>> 'This Is Title Case'.istitle()
True
>>> 'This Is Title Case 123'.istitle()
True
>>> 'This Is not Title Case'.istitle()
False
>>> 'This Is NOT Title Case Either'.istitle()
False

تفيد توابع السلاسل النصية isX()‎ حينما نريد التحقق من مدخلات المستخدم، فالبرنامج الآتي يطلب من المستخدم إدخال عمره وكلمة مرور صالحة:

while True:
    print('Enter your age:')
    age = input()
    if age.isdecimal():
        break
    print('Please enter a number for your age.')

while True:
    print('Select a new password (letters and numbers only):')
    password = input()
    if password.isalnum():
        break
    print('Passwords can only have letters and numbers.')

طلبنا من المستخدم في أول حلقة while أن يدخل عمره، ونخزنه في المتغير age. إذا كانت قيمة age هي قيمة عددية صحيحة، فسنخرج من أول حلقة while وندخل في الثانية التي تسأله عن كلمة المرور. وإلا فسنسأل المستخدم عن عمره مجددًا.

في حلقة while الثانية طلبنا كلمة المرور وخزناها في المتغير password، وسنخرج من الحلقة إن كانت كلمة المرور تتألف من أرقام أو أحرف، وإلا فسنطلب من المستخدم إدخال كلمة مرور صالحة مجددًا.

Enter your age:
forty two
Please enter a number for your age.
Enter your age:
42
Select a new password (letters and numbers only):
secr3t!
Passwords can only have letters and numbers.
Select a new password (letters and numbers only):
secr3t

تمكّنا من التحقق من صلاحية مدخلات المستخدم في المثال السابق باستخدام التابعين isdecimal()‎ و isalnum()، ولم نقبل كتابة forty two بل قبلنا 42، ولم نقبل secr3t!‎ بل قبلنا secr3t.

التابعان startswith()‎ و endswith()‎

يعيد التابعان startswith()‎ و endswith()‎ القيمة True إن بدأت أو انتهت السلسلة النصية التي استدعت عليها (على التوالي) بالسلسلة النصية الممررة إلى التابع؛ وإلا فستعيد False:

>>> 'Hello, world!'.startswith('Hello')
True
>>> 'Hello, world!'.endswith('world!')
True
>>> 'abc123'.startswith('abcdef')
False
>>> 'abc123'.endswith('12')
False
>>> 'Hello, world!'.startswith('Hello, world!')
True
>>> 'Hello, world!'.endswith('Hello, world!')
True

هذه التوابع هي بديل مفيد لعامل المساواة == إذا أردنا التحقق من الجزء الأول أو الأخير من السلسلة النصية فقط، بدلًا من مقارنتها كلها.

التابعان join()‎ و split()‎

يفيد التابع join() حينما يكون لدينا قائمة فيها سلاسل نصية ونريد أن نجمعها كلها مع بعضها بعضًا في سلسلة نصية واحدة؛ ويستدعى التابع join() على سلسلة نصية، ويقبل معاملًا هو قائمة list فيها سلاسل نصية، ويعيد سلسلةً نصيةً تساوي دمج السلاسل النصية كلها:

>>> ', '.join(['cats', 'rats', 'bats'])
'cats, rats, bats'
>>> ' '.join(['My', 'name', 'is', 'Simon'])
'My name is Simon'
>>> 'ABC'.join(['My', 'name', 'is', 'Simon'])
'MyABCnameABCisABCSimon'

لاحظ أن السلسلة النصية التي استدعينا عليها التابع join()‎ أصبحت موجودة بين كل سلسلتين نصيتين في القائمة الممررة كوسيط. فمثلًا حين استدعاء join(['cats', 'rats', 'bats']) على السلسلة النصية ‎', '‎ فيكون الناتج هو ‎'cats, rats, bats'.

تذكر أن التابع join() يستدعى على سلسلة نصية ونمرر إليه قائمة، وليس العكس.

يفعل التابع split()‎ عكس فعل التابع join()‎ تمامًا: يستدعى على سلسلة نصية وتعيد قائمةً من السلاسل النصية:

>>> 'My name is Simon'.split()
['My', 'name', 'is', 'Simon']

ستُقسَم السلسلة النصية 'My name is Simon' افتراضيًا عند كل محرف يمثل فراغًا (سواءً كان فراغًا عاديًا ' ' أو محرف جدولة tab أو سطرًا جديدًا newline)، ولن تضمن الفراغات في السلاسل النصية الموجودة في القائمة المعادة من استدعاء هذا التابع.

يمكننا تمرير محرف الفصل إلى التابع split()‎ لتحديد محرف آخر غير محارف الفراغات:

>>> 'MyABCnameABCisABCSimon'.split('ABC')
['My', 'name', 'is', 'Simon']
>>> 'My name is Simon'.split('m')
['My na', 'e is Si', 'on']

من الشائع استدعاء التابع split()‎ لتقسيم سلسلة نصية متعددة الأسطر في مكان وقوع محرف السطر الجديد:

>>> spam = '''Dear Alice,
How have you been? I am fine.
There is a container in the fridge
that is labeled "Milk Experiment."

Please do not drink it.
Sincerely,
Bob'''
>>> spam.split('\n')
['Dear Alice,', 'How have you been? I am fine.', 'There is a container in the
fridge', 'that is labeled "Milk Experiment."', '', 'Please do not drink it.',
'Sincerely,', 'Bob']

يسمح لنا استدعاء التابع split()‎ مع تمرير محرف السطر الجديد '‎\n' إلى تقسيم سلسلة نصية متعددة الأسطر إلى قائمة يمثل فيها كل عنصر سطرًا من الأسطر.

تقسيم السلاسل النصية باستخدام التابع partition()‎

يمكن أن يقسم التابع partition()‎ سلسلةً نصية إلى أقسام، ويعمل بتمرير سلسلة نصية إليه كفاصل، التي سيبحث عنها في السلسلة النصية التي استدعي عليها، ويعيد صفًا tuple فيه السلسلة النصية التي تسبق الفاصل، والفاصل، والسلسلة النصية التي تلي الفاصل:

>>> 'Hello, world!'.partition('w')
('Hello, ', 'w', 'orld!')
>>> 'Hello, world!'.partition('world')
('Hello, ', 'world', '!')

إذا احتوت السلسلة النصية التي تستدعي التابع partition()‎ عليها على أكثر من تكرار للفاصل، فستقسم السلسلة النصية عند أول وقوع له فقط:

>>> 'Hello, world!'.partition('o')
('Hell', 'o', ', world!')

وإن لم يعثر على الفاصل في السلسلة النصية، فستعاد السلسلة النصية التي استدعي عليها التابع كأول عنصر في الصف، وستكون السلسلتان النصيتان الباقيتان فارغتين:

>>> 'Hello, world!'.partition('XYZ')
('Hello, world!', '', '')

يمكننا استخدام نشر المتغيرات بالإسناد الجماعي لإسناد السلاسل النصية المعادة إلى ثلاثة متغيرات:

>>> before, sep, after = 'Hello, world!'.partition(' ')
>>> before
'Hello,'
>>> after
'world!'

يفيد التابع partition()‎ إن كنت تحتاج إلى الحصول على السلسلة النصية التي تسبق فاصلًا محددًا، والفاصل نفسه، والسلسلة النصية التي تلي ذاك الفاصل.

محاذاة النصوص عبر rjust()‎ و ljust()‎ و center()‎

يعيد التابعان rjust()‎ و ljust()‎ سلسلةً نصية محاطة بفراغات افتراضيًا. يمثل أول وسيط يمرر إلى تلك التوابع قيمةً لعدد الفراغات المحيطة بالسلسلة النصية:

>>> 'Hello'.rjust(10)
'     Hello'
>>> 'Hello'.rjust(20)
'              Hello'
>>> 'Hello, World'.rjust(20)
'         Hello, World'
>>> 'Hello'.ljust(10)
'Hello     '

التابع 'Hello'.rjust(10)‎ يعني أننا نريد محاذاة السلسلة النصية 'Hello' إلى اليمين ويكون طول السلسلة النصية الكاملة هو 10. ولما كانت 'Hello' هي 5 محارف، فستضاف 5 فراغات على يسارها، مما يؤدي إلى إعادة سلسلة نصية طولها 10 محارف وتكون فيها محاذاة 'Hello' على اليمين.

وسيطٌ اختياري للتابعين rjust() و ljust() يحدد محرفًا للملء بخلاف الفراغ:

>>> 'Hello'.rjust(20, '*')
'***************Hello'
>>> 'Hello'.ljust(20, '-')
'Hello---------------'

التابع center()‎ يعمل مثل التابعين ljust()‎ و rjust() لكنه يوسِّط النص بدلًا من محاذاته إلى اليسار أو اليمين:

>>> 'Hello'.center(20)
'       Hello        '
>>> 'Hello'.center(20, '=')
'=======Hello========'

تفيد هذه التوابع حينما نريد طباعة جداول من البيانات ويكون لها تباعد صحيح. اكتب البرنامج الآتي واحفظه في الملف picnicTable.py:

def printPicnic(itemsDict, leftWidth, rightWidth):
    print('PICNIC ITEMS'.center(leftWidth + rightWidth, '-'))
    for k, v in itemsDict.items():
        print(k.ljust(leftWidth, '.') + str(v).rjust(rightWidth))

picnicItems = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000}
printPicnic(picnicItems, 12, 5)
printPicnic(picnicItems, 20, 6)

عرفنا الدالة printPicnic()‎ التي تأخذ قاموسًا كمعامل لها، واستخدمنا التوابع center()‎ و ljust()‎ و rjust()‎ لطباعة المعلومات بصيغة جميلة تشبه الجدول.

القاموس الذي سنمرره إلى الدالة printPicnic()‎ هو picnicItems، ولدينا في القاموس picnicItems 4 صندويشات و 12 تفاحة و 4 كاسات و 8,000 كعكة (نعم ثمانية آلاف!). نرغب بتنظيم هذه المعلومات في عمودين، ونضع اسم العنصر على اليسار والكمية على اليمين.

لفعل ذلك نحتاج إلى أن نقرر كم سيكون عرض العمودين الأيسر والأيمن؛ لذا نحتاج إلى تمرير هاتين القيمتين مع القاموس إلى الدالة printPicnic() عبر المعاملين leftWidth لعرض العمود الأيسر من الجدول و rightWidth لعرض العمود الأيمن من الجدول.

ستطبع الدالة printPicnic()‎ عنوانًا متوسطًا فوق العنصر PICNIC ITEMS، ثم ستمر على عناصر القاموس وتطبع كل زوج من القيم في سطر وتحاذي المفتاح على اليسار وله حاشية متألفة من فواصل، والقيمة على اليمين ولها حاشية متألف من فراغات.

بعد تعريف الدالة printPicnic()‎ سنعرف القاموس picnicItems ونستدعي الدالة printPicnic()‎ مرتين، مع تمرير عرض مختلف للعمودين الأيسر والأيمن؛ يكون فيها عرض العمود الأيسر 12 والأيمن 5 في المرة الأولى، ثم 20 و 6 في المرة الثانية.

---PICNIC ITEMS--
sandwiches..    4
apples......   12
cups........    4
cookies..... 8000
-------PICNIC ITEMS-------
sandwiches..........     4
apples..............    12
cups................     4
cookies.............  8000

رأينا كيف استفدنا من rjust()‎ و ljust() و center()‎ لطباعة سلاسل نصية منسقة تنسيقًا جميلًا، حتى لو لم نكن نعرف كم سيكون العدد الدقيق لمحارف السلسلة النصية التي سنطبعها.

حذف الفراغات عبر strip()‎ و rstrip()‎ و lstrip()‎

نحتاج أحيانًا إلى حذف الفراغات البيضاء (التي هي محارف الفراغ العادي ومسافة الجدولة tab والسطر الجديد newline) من الطرف الأيسر أو الأيمن أو من كلا طرفي السلسلة النصية.

سيعيد التابع strip()‎ سلسلةً نصيةً جديدةً لا تحتوي على أي فراغات في بدايتها أو نهايتها، بينما يحذف التابعان lstrip()‎ و rstrip()‎ الفراغات البيضان من الطرف الأيسر أو الأيمن على التوالي:

>>> spam = '    Hello, World    '
>>> spam.strip()
'Hello, World'
>>> spam.lstrip()
'Hello, World    '
>>> spam.rstrip()
'    Hello, World'

يمكننا تمرير وسيط اختياري يحتوي على المحارف التي نريد حذفها بدلًا من الفراغات:

>>> spam = 'SpamSpamOliveSpamEggsSpamSpam'
>>> spam.strip('ampS')
'OliveSpamEggs'

تمرير الوسيط 'ampS' إلى التابع strip()‎ سيؤدي إلى حذف جميع تكرارات المحارف a و m و p و S من بداية ونهاية السلسلة النصية spam. لاحظ أن ترتيب الأحرف في السلسلة النصية الممررة إلى التابع strip()‎ لا يهم، فكتابة strip('ampS')‎ تكافئ كتابة strip('mapS')‎ أو strip('Spam')‎.

القيم العددية للأحرف مع الدالتين ord()‎ و chr()‎

تخزن الحواسيب البيانات على هيئة بايتات، وهي سلاسل من البتات بنظام العد الثنائي، وهذا يعني أننا نحتاج إلى تحويل النصوص إلى أرقام لكي نستطيع تخزينها؛ ولهذا يكون لكل محرف character قيمة رقمية مرتبطة به تسمى Unicode code point. فمثلًا القيمة الرقمية 65 تمثل 'A'، والقيمة 52 تمثل '4'، والقيمة 33 تمثل '!'.

يمكننا استخدام الدالة ord()‎ للحصول على القيمة الرقمية لسلسلة نصية تحتوي محرفًا واحدًا، والدالة chr()‎ للحصول على المحرف الذي وفرنا قيمته الرقمية:

>>> ord('A')
65
>>> ord('4')
52
>>> ord('!')
33
>>> chr(65)
'A'

تفيد هذه الدوال حينما نحتاج إلى ترتيب المحارف أو إجراء عمليات رياضية عليها:

>>> ord('B')
66
>>> ord('A') < ord('B')
True
>>> chr(ord('A'))
'A'
>>> chr(ord('A') + 1)
'B'

هنالك تفاصيل كثيرة عن يونيكود وأرقام المحارف، لكنها خارجة عن سياق هذه السلسلة.

نسخ ولصق السلاسل النصية باستخدام الوحدة pyperclip

تمتلك الوحدة pyperclip الدالتين copy() و paste()‎ التي يمكنها إرسال واستقبال النص من حافظة نظام التشغيل الذي تستعمله. إذ يسهل عليك إرسال ناتج برنامجك إلى الحافظة أن تلصقه في رسالةٍ بريدية أو في محرر النصوص أو غيره من البرمجيات.

تشغيل سكربتات بايثون خارج محرر Mu

شغلنا كل سكربتات بايثون التي كتبناها حتى الآن باستخدام الصدفة التفاعلية ومحرر الشيفرات Mu؛ لكننا لسنا بحاجة إلى فتح محرر Mu في كل مرة نريد فيها تنفيذ سكربتات بايثون التي كتبناها. لحسن الحظ هنالك طرائق تسهل علينا تشغيل سكربتات بايثون، لكن هذه الطرائق تختلف من نظام ويندوز إلى MacOS ولينكس، وهي مشروحة في المقال الأول من هذه السلسلة، لذا أنصحك بالانتقال إلى المقال الأول لتعرف كيف تشغل سكربتات بايثون بسهولة على نظامك، وكيف تمرر خيارات سطر الأوامر command line arguments إليها، لاحظ أنك لا تستطيع تمرير خيارات سطر الأوامر باستخدام محرر Mu.

الوحدة pyperclip غير مضمنة في بايثون، لذا عليك تثبيتها باتباع التعليمات المذكورة في المقال الأول من السلسلة أيضًا. يمكنك أن تدخل ما يلي في الصدفية التفاعلية بعد تثبيت الوحدة pyperclip:

>>> import pyperclip
>>> pyperclip.copy('Hello, world!')
>>> pyperclip.paste()
'Hello, world!'

إذا غيّر أحد البرامج محتويات الحافظة فستعيدها الدالة paste()، فانسخ مثلًا عبارةً من المتصفح أو من محرر الشيفرات ثم استدعِ الدالة paste():

>>> pyperclip.paste()
'For example, if I copied this sentence to the clipboard and then called
paste(), it would look like this:'

مشروع: حافظة فيها رسائل تلقائية

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

الخطوة 1: تصميم البرنامج وبنى المعطيات

نرغب أن نشغل هذا البرنامج من سطر الأوامر مع تمرير وسيط إليه الذي يحتوي على كلمة مفتاحية واحدة مثل «موافق» agree أو «مشغول» busy. وستُنسَخ الرسالة المرتبطة بتلك الكلمة المفتاحية إلى الحافظة لكي يستطيع المستخدم لصقها مباشرةً في الردود، وبهذا يمكن أن نستعمل عبارات طويلة دون الحاجة إلى إعادة كتابتها كل مرة.

مشاريع الفصول

هذا هو أول «مشروع» في هذه السلسلة ومن الآن فصاعدًا ستجد في كل مقال عددًا من المشاريع التي تستعمل المفاهيم المشروحة في ذاك المقال وتبدأ هذه المشاريع من الصفر حتى الحصول على برنامج يعمل تمامًا، وأنصحك وبشدة أن تطبق أولًا بأول ولا تكتفي بالقراءة فقط.

افتح نافذة محرر جديدة واحفظ البرنامج الآتي باسم mclip.py، ستحتاج إلى بدء البرنامج مع الرمز ‎#! (الذي يسمى shebang، راجع المقال الأول من هذه السلسلة)، ثم كتابة تعليق يشرح باختصار ما يفعله البرنامج.

لمّا كنّا نريد أن نربط كل جملة نصية بمفتاح، فمن المنطقي أن نخزنها في قاموس، والذي سيكون هو بنية المعطيات الأساسية في برنامجنا:

#! python3
# mclip.py - A multi-clipboard program.

TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
        'busy': """Sorry, can we do this later this week or next week?""",
        'upsell': """Would you consider making this a monthly donation?"""}

الخطوة 2: التعامل مع وسائط سطر الأوامر

تخزن الوسائط الممررة من سطر الأوامر command line arguments في المتغير sys.argv (راجع المقال الأول من هذه السلسلة لمزيدٍ من المعلومات حول استخدام وسائط سطر الأوامر في نظامك).

يجب أن يكون أول عنصر في القائمة sys.argv هو سلسلة نصية تمثل اسم الملف 'mclip.py' أما العنصر الثاني في القائمة فهو قيمة الوسيط الأول الممرر عبر سطر الأوامر.

سيمثل الوسيط الأول قيمة مفتاح العبارة التي نريد نسخها، ولأننا نريد إجبار المستخدم على تمرير وسيط في سطر الأوامر، فستعرض رسالةً إلى المستخدم إن نسي توفيره للبرنامج (إذا كانت القائمة sys.argv تحتوي على أقل من قيمتين):

#! python3
# mclip.py - A multi-clipboard program.

TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
        'busy': """Sorry, can we do this later this week or next week?""",
        'upsell': """Would you consider making this a monthly donation?"""}

import sys
if len(sys.argv) < 2:
    print('Usage: python mclip.py [keyphrase] - copy phrase text')
    sys.exit()

keyphrase = sys.argv[1]    # أول وسيط في سطر الأوامر هو مفتاح العبارة التي نريد نسخها

الخطوة 3: نسخ العبارة الصحيحة

أصبح لدينا مفتاح العبارة مخزنًا في المتغير keyphrase، لذا ستحتاج إلى التحقق إن كان هذا المفتاح موجودًا في القاموس TEXT، فإن كان موجودًا فسننسخ العبارة المرتبطة بهذا المفتاح إلى الحافظة باستخدام الدالة pyperclip.copy()‎، ولمّا كنّا نستعمل الوحدة pyperclip فسنحتاج إلى استيرادها أيضًا.

لاحظ أننا لسنا بحاجة إلى إنشاء المتغير keyphrase، إذ نستطيع استخدام sys.argv[1]‎ في أي مكان استعملنا فيه keyphrase لكن وجود متغير باسم keyphrase سيسهل قراءة الشيفرة كثيرًا:

#! python3
# mclip.py - A multi-clipboard program.

TEXT = {'agree': """Yes, I agree. That sounds fine to me.""",
        'busy': """Sorry, can we do this later this week or next week?""",
        'upsell': """Would you consider making this a monthly donation?"""}

import sys, pyperclip
if len(sys.argv) < 2:
    print('Usage: py mclip.py [keyphrase] - copy phrase text')
    sys.exit()

keyphrase = sys.argv[1]    # first command line arg is the keyphrase

if keyphrase in TEXT:
    pyperclip.copy(TEXT[keyphrase])
    print('Text for ' + keyphrase + ' copied to clipboard.')
else:
    print('There is no text for ' + keyphrase)

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

السكربت السابق كامل، ويمكننا اتباع التعليمات الموجودة في المقال الأول لتشغيل البرامج السطرية بسهولة، وأصبح لدينا الآن طريقة سريعة لنسخ الرسائل الطويلة إلى الحافظة بسهولة. لا تنسَ أن تعدل قيمة القاموس TEXT في كل مرة تحتاج فيها إلى إضافة عبارة جديدة.

إذا كنت على نظام ويندوز فيمكنك إنشاء ملف دفعي batch file لتشغيل البرنامج باستخدام نافذة تشغيل البرامج Run (بالضغط على Win+R). أدخل ما يلي في محرر النصوص واحفظه باسم mclip.bat في مجلد C:\Windows:

@py.exe C:\path_to_file\mclip.py %*
@pause

يمكننا بعد إنشاء الملف الدفعي أن نشغل برنامجنا بالضغط على Win+R ثم كتابة mclip key_phrase.

مشروع: إضافة قائمة منقطة إلى ويكيبيديا

حينما تعدل مقالًا في ويكيبيديا، فيمكنك إنشاء قائمة منقطة بوضع كل عنصر من عناصر القائمة في سطر خاص به ووضع نجمة قبله؛ لكن لنقل أن لدينا قائمة طويلة جدًا من العناصر التي تريد تحويلها إلى قائمة منقطة، هنا يمكنك أن تقضي بعض الوقت بإضافة رمز النجمة في بداية كل سطر يدويًا أو تكتب سكربتًا يؤتمت هذه العملية.

يأخذ السكربت bulletPointAdder.py النص الموجود في الحافظة ويضيف نجمة وفراغًا إلى بداية كل سطر فيه، ثم يحفظ النص الناتج إلى الحافظة. فمثلًا إذا نسخت النص الآتي من صفحة ويكيبيديا المسماة «قائمة القوائم» List of Lists of Lists إلى الحافظة:

Lists of animals
Lists of aquarium life
Lists of biologists by author abbreviation
Lists of cultivars

ثم شغلت البرنامج bulletPointAdder.py فستصبح محتويات الحافظة كما يلي:

* Lists of animals
* Lists of aquarium life
* Lists of biologists by author abbreviation
* Lists of cultivars

ونستطيع الآن لصق النص الناتج إلى ويكيبيديا كقائمة منقطة.

الخطوة 1: النسخ واللصق من وإلى الحافظة

نريد من برنامج bulletPointAdder.py أن يفعل ما يلي:

  1. يأخذ النص الموجود في الحافظة
  2. يجري عليه عمليات
  3. يحفظ الناتج في الحافظة

الخطوة الثانية صعبة بعض الشيء، لكن الخطوات 1 و 3 سهلة جدًا: كل ما علينا فعله هو استدعاء الدالتين pyperclip.copy()‎ و pyperclip.paste()‎. لننشِئ برنامجًا ينفذ الخطوات 1 و 3:

#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.

import pyperclip
text = pyperclip.paste()

# TODO: فصل الأسطر وإضافة رمز النجمة

pyperclip.copy(text)

التعليق الذي يبدأ بالكلمة TODO هو تذكير لنا أن علينا إكمال هذا الجزء من البرنامج، لذا ستكون الخطوة القادمة هي برمجة هذا الجزء.

الخطوة 2: فصل الأسطر وإضافة النجمة

ناتج استدعاء pyperclip.paste() يعيد النص الموجود في الحافظة كسلسة نصية واحدة، إذ سيبدو مثال «قائمة القوائم» الذي نسخناه على الشكل الآتي:

'Lists of animals\nLists of aquarium life\nLists of biologists by author
abbreviation\nLists of cultivars'

يوجد المحرف ‎\n في السلسلة النصية السابقة لعرضها على عدة أسطر حين لصقها من الحافظة، لاحظ أن السلسلة النصية السابقة «متعددة» الأسطر لوجود محرف التهريب ‎\n. ستحتاج إلى إضافة نجمة إلى بداية كل سطر من الأسطر السابقة.

يمكنك أن تكتب شيفرة تبحث عن المحرف ‎\n وتضيف نجمةً قبله، أو أن تستعمل التابع split() لإعادة قائمة من السلاسل النصية يمثل كل عنصر فيها سطرًا من السلسلة النصية الأصلية، وبعد ذلك تضيف النجمة قبل كل عنصر:

#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.

import pyperclip
text = pyperclip.paste()

# فصل الأسطر وإضافة النجمة
lines = text.split('\n')
for i in range(len(lines)):    # المرور على جميع عناصر القائمة
    lines[i] = '* ' + lines[i] # إضافة نجمة وفراغ قبل كل عنصر

pyperclip.copy(text)

قسمنا النص في مكان السطر الجديد ليمثل كل عنصر في القائمة سطرًا واحدًا، وخزّنا الناتج في المتغير lines، ثم مررنا على عناصر القائمة lines، ولكل عنصر أضفنا نجمة وفراغًا في بداية السطر. أصبحت لدينا الآن قائمة باسم lines فيها عناصر القائمة وقبل كل عنصر رمزُ النجمة.

الخطوة 3: دمج الأسطر المعدلة

تحتوي القائمة lines على الأسطر المعدلة التي تبدأ بالنجمة، لكن الدالة pyperclip.copy() تتعامل مع السلاسل النصية وليس مع القوائم، لذا نحتاج إلى تحويل القائمة إلى سلسلة نصية، وذلك بجمع عناصر مع بعضها بعضًا عبر التابع join()‎:

#! python3
# bulletPointAdder.py - Adds Wikipedia bullet points to the start
# of each line of text on the clipboard.

import pyperclip
text = pyperclip.paste()

# فصل الأسطر وإضافة النجمة
lines = text.split('\n')
for i in range(len(lines)):    # المرور على جميع عناصر القائمة
    lines[i] = '* ' + lines[i] # إضافة نجمة وفراغ قبل كل عنصر

text = '\n'.join(lines)
pyperclip.copy(text)

حين تشغيل البرنامج السابق فسيبدل محتويات الحافظة ويضيف نجمةً قبل كل سطر موجود فيها. أصبح البرنامج جاهزًا للتجربة.

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

برنامج قصير: اللغة السرية " أين الحلقة السابقة؟ يحتاج المثال إلى إعادة صياغة كليًا"

هنالك لغة سرية يلعب الأطفال ويستعملونها اسمها Pig Latin، وهي تحريف للكلمات الإنكليزية بقواعد بسيطة. فلو بدأت الكلمة بحرف متحرك فتضاف الكلمة yay إلى نهايتها، وإذا بدأت الكلمة بحرف ساكن أو تركيب ساكن (مثل ch أو gr) فسينقل ذاك الساكن إلى نهاية البرنامج مع إلحاق ay.

لنكتب برنامجًا للتحويل إلى اللغة السرية يطبع شيئًا يشبه المثال الآتي:

Enter the English message to translate into Pig Latin:
My name is AL SWEIGART and I am 4,000 years old.
Ymay amenay isyay ALYAY EIGARTSWAY andyay Iyay amyay 4,000 yearsyay oldyay.

يستعمل برنامجنا التوابع التي تعرفنا عليها في هذا المقال احفظ السكربت الآتي في ملف باسم pigLat.py:

# English to Pig Latin
print('Enter the English message to translate into Pig Latin:')
message = input()

VOWELS = ('a', 'e', 'i', 'o', 'u', 'y')

pigLatin = [] # قائمة الكلمات في اللغة السرية
for word in message.split():
    # فصل الكلمات التي لا تبدأ بأحرف
    prefixNonLetters = ''
    while len(word) > 0 and not word[0].isalpha():
        prefixNonLetters += word[0]
        word = word[1:]
    if len(word) == 0:
        pigLatin.append(prefixNonLetters)
        continue

    # فصل الكلمات التي لا تنتهي بحرف
    suffixNonLetters = ''
    while not word[-1].isalpha():
        suffixNonLetters += word[-1]
        word = word[:-1]

    # تذكر حالة الأحرف
    wasUpper = word.isupper()
    wasTitle = word.istitle()

    word = word.lower() # تحويل الكلمة إلى الحالة الصغيرة لتحويلها

    # فصل الأحرف الساكنة في بداية الكلمة
    prefixConsonants = ''
    while len(word) > 0 and not word[0] in VOWELS:
        prefixConsonants += word[0]
        word = word[1:]

    # إضافة اللاحقة الخاصة باللغة السرية إلى نهاية الكلمة
    if prefixConsonants != '':
        word += prefixConsonants + 'ay'
    else:
        word += 'yay'

    # إعادة الكلمة إلى حالتها الأصلية
    if wasUpper:
        word = word.upper()
    if wasTitle:
        word = word.title()

    # إضافة الرموز التي استخرجناها سابقًا
    pigLatin.append(prefixNonLetters + word + suffixNonLetters)

# جمع الكلمات مع بعضها إلى سلسلة نصية
print(' '.join(pigLatin))

لنلقِ نظرةً على الشيفرة من بدايتها:

# English to Pig Latin
print('Enter the English message to translate into Pig Latin:')
message = input()

VOWELS = ('a', 'e', 'i', 'o', 'u', 'y')

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

أنشأنا بعد ذلك المتغير pigLatin لتخزين الكلمات بعد تحويلها إلى اللغة السرية:

pigLatin = [] # قائمة الكلمات في اللغة السرية
for word in message.split():
    # فصل الكلمات التي لا تبدأ بأحرف
    prefixNonLetters = ''
    while len(word) > 0 and not word[0].isalpha():
        prefixNonLetters += word[0]
        word = word[1:]
    if len(word) == 0:
        pigLatin.append(prefixNonLetters)
        continue

نريد أن نعالج كل كلمة بمفردها، لذا استعملنا التابع message.split() للحصول على قائمة بالكلمات، فالسلسلة النصية ‎'My name is AL SWEIGART and I am 4,000 years old.'‎ مع التابع split()‎ سيعيد الناتج.

 ‎['My', 'name', 'is', 'AL', 'SWEIGART', 'and', 'I', 'am', '4,000', 'years', 'old.']‎.

سنحتاج إلى الاحتفاظ بأي رموز في بداية ونهاية كل كلمة، فلو كانت لدينا الكلمة 'old.‎' فستحول إلى 'oldyay.‎' بدلًا من 'old.yay'. سنحتفظ بهذه الرموز في متغير باسم prefixNonLetters.

    # فصل الكلمات التي لا تنتهي بحرف
    suffixNonLetters = ''
    while not word[-1].isalpha():
        suffixNonLetters += word[-1]
        word = word[:-1]

سنبدأ حلقة تكرار تستدعي التابع isalpha() على أول حرف من الكلمة لتتأكد إن كان علينا إزالة حرف من الكلمة وإضافته إلى نهاية السلسلة النصية prefixNonLetters، وإذا كانت الكلمة كلها تتألف من رموز أو أرقام مثل '4,000' فسنضيفها كما هي إلى القائمة pigLatin وننتقل إلى الكلمة التالية لنحولها إلى اللغة السرية. سنحتفظ بالرموز في نهاية السلسلة النصية word، وهذا يشبه الحلقة السابقة.

نرغب أن يتذكر البرنامج حالة الأحرف للكلمة لكي نستطيع استعادتها بعد عملية التحويل:

    # تذكر حالة الأحرف
    wasUpper = word.isupper()
    wasTitle = word.istitle()

    word = word.lower() # تحويل الكلمة إلى الحالة الصغيرة لتحويلها

سنستخدم الكلمة المخزنة في المتغير word بحالتها الصغيرة حتى نهاية دورة حلقة for.

لتحويل إحدى الكلمات مثل sweigart إلى eigart-sway فسنتحتاج إلى إزالة جميع الأحرف الساكنة من بداية الكلمة word:

    # فصل الأحرف الساكنة في بداية الكلمة
    prefixConsonants = ''
    while len(word) > 0 and not word[0] in VOWELS:
        prefixConsonants += word[0]
        word = word[1:]

استخدمنا حلقة تكرار تشبه الحلقة التي أزالت الرموز من بداية الكلمة word، لكننا الآن نزيد الأحرف الساكنة ونخزنها في متغير باسم prefixConsonants.

إذا لم يبقَ أي حرف ساكن في بداية الكلمة، فهذا يعني أنها أصبحت كلها في المتغير prefixConsonants، ويمكننا الآن جمع قيمة ذاك المتغير مع السلسلة النصية 'ay' في نهاية المتغير word. أما خلاف ذلك فهذا يعني أن word تبدأ بحرف صوتي، وسنحتاج إلى إضافة 'yay':

    # إضافة اللاحقة الخاصة باللغة السرية إلى نهاية الكلمة
    if prefixConsonants != '':
        word += prefixConsonants + 'ay'
    else:
        word += 'yay'

تذكر أننا جعلنا الكلمة بحالة الأحرف الصغيرة word = word.lower(). أما إذا كانت القيمة word بحالة الأحرف الكبيرة أو نسق العناوين Title case فعلينا تحويلها إلى حالتها الأصلية:

   # إعادة الكلمة إلى حالتها الأصلية
    if wasUpper:
        word = word.upper()
    if wasTitle:
        word = word.title()

وفي نهاية دورة حلقة for سنضيف الكلمة مع أي رموز سابقة أو لاحقة إلى القائمة pigLatin:

    # إضافة الرموز التي استخرجناها سابقًا
    pigLatin.append(prefixNonLetters + word + suffixNonLetters)

# جمع الكلمات مع بعضها إلى سلسلة نصية
print(' '.join(pigLatin))

بعد نهاية حلقة التكرار فسنجمع عناصر القائمة pigLatin مع بعضها باستخدام التابع join()، وستمرر سلسلة نصية واحدة إلى الدالة print() لطباعة الجملة السرية.

الخلاصة

النصوص هي أكثر أنواع البيانات شيوعًا، وتأتي بايثون مع مجموعة من توابع معالجة النصوص المفيدة. ستستخدم الفهرسة والتقسيم وتوابع السلاسل النصية في كل برنامج بايثون تكتبه تقريبًا.

صحيحٌ أن البرامج التي تكتبها الآن لا تبدو معقدة جدًا، فهي لا تحتوي على واجهات مستخدم رسومية فيها صور ونصوص ملونة، فكل ما نستعمله هو الدالة print()‎ لطباعة النصوص و input()‎ لقبول مدخلات المستخدم؛ لكن يستطيع المستخدم إدخال مجموعة كبيرة من النصوص عبر نسخها إلى الحافظة، مما يفيد كثيرًا في معالجة كميات كبيرة من النصوص. وصحيحٌ أن هذه البرامج لا تملك واجهات رسومية جميلة لكنها تفعل الكثير بجهدٍ قليل.

طريقة أخرى لمعالجة كمية كبيرة من النصوص هي كتابة الملفات وقراءتها من نظام الملفات المحلي مباشرةً، وسنتعلم كيفية فعل ذلك في مقال لاحق. لقد شرحنا حتى الآن المفاهيم الأساسية في برمجة بايثون! ستتعلم مفاهيم جديدة في بقية هذه السلسلة، لكن يفترض أنك تعرف ما يكيفك لكتابة برامج مفيدة تستطيع عبرها أتمتة المهام.

قد تظن أنك لا تمتلك المعرفة الكافية في بايثون لتنزيل صفحات الويب أو تحديث جداول البيانات أو إرسال الرسائل البريدية، لكن هنا يأتي دور الوحدات الخارجية في بايثون، التي توفر لك ما تحتاج إليه لفعل كل ذلك وأكثر منه، والتي سنتعلمها سويةً في المقالات القادمة.

مشاريع تدريبية

لكي تتدرب، اكتب برامج لتنفيذ المهام الآتية.

طابع جداول

اكتب دالةً اسمها printTable()‎ تقبل قائمةً فيها قوائم تحتوي على سلاسل نصية، وتعرضها في جدول منسق تكون فيه محاذاة الأعمدة إلى اليمين. افترض أن لكل القوائم الداخلية العدد نفسه من السلاسل النصية، فمثلًا:

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]

يجب أن تطبع الدالة printTable()‎ ما يلي:

   apples Alice  dogs
  oranges   Bob  cats
 cherries Carol moose
   banana David goose

تلميح: يجب أن تبحث عن أطول سلسلة نصية في كل قائمة داخلية، كي يتسع العمود لجميع السلاسل النصية. يمكنك تخزين العرض الأقصى لكل عمود في قائمة من الأرقام.

يمكن أن تبدأ الدالة printTable() بالسطر colWidths = [0] * len(tableData)‎ الذي سينشِئ قائمةً تحتوي على الرقم 0 يساوي عدد القوائم الداخلية الموجودة في القائمة tableData، وبالتالي ستخزن عرض أطول سلسلة نصية في tableData[0] في العنصر colWidths[0]‎، وعرض أطول سلسلة نصية في القائمة tableData[1] في العنصر colWidths[1] وهلم جرًا… ثم يمكنك الحصول على أكبر قيمة في القائمة colWidths لتعرف القيمة التي ستمررها كعرض إلى التابع rjust()‎.

ترجمة -بتصرف- للفصل Manipulating Strings من كتاب Automate the Boring Stuff with Python.

اقرأ أيضًا


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...