= 'Adam'
name = "Makkah, Saudi Arabia" address
6 الملفات النصية
لم يقتصر عمل الحاسب على العمليات الحسابية بين الأرقام فحسب، بل امتدَّ ليصل إلى معالجة النصوص (Text Processing) التي تأتي بأشكال مختلفة منها: محادثات وسائل التواصل الاجتماعي، ورسائل البريد الإلكتروني، ومقالات وكتب وموسوعات وصفحات ومواقع الشبكة.
فأوجه معالجة النصوص الحرة كثيرة منها: الفهرسة والعد والتصنيف والترتيب لتسهيل البحث والاسترجاع. وكذلك التعديل بالاستبدال، والتصحيح التلقائي، والرسم؛ سواءٌ على الشاشة أو في الطباعة.
وتوجد ثلاثة طرق لإنشاء النص في بايثون:
- بعلامة اقتباس مفردة:
'السلام عليكم!'
- أو بعلامة اقتباس مزدوجة:
"السلام عليكم!"
وليس بينهما فرق. - أو بعلامة اقتاس مكررة ثلاثة مرات:
"""السلام عليكم!"""
للنص الجاري على أكثر من سطر.
على سبيل المثال:
تأمل إنشاء هذا النص الذي يبتدئ في السطر الأول ويمتد لأربعة أسطر:
= """السلام عليكم ورحمة الله وبركاته,
message أبشرك بأنك قطعت شوطًا كبيرًا.
شكرًا لك.
أخوك آدم.
"""
print(message)
السلام عليكم ورحمة الله وبركاته,
أبشرك بأنك قطعت شوطًا كبيرًا.
شكرًا لك.
أخوك آدم.
حقيقة النص
يتم تمثيل النص في بايثون بالنوع str
(من String وتعني خيط) وهو سلسلة رموز (وهي التي نضعها بين علامتي التنصيص ""
) لكن الذي يحصل في الواقع أن مفسِّر لغة بايثون يترجمها إلى أرقام تقابلها في جدول الترميز العالمي Unicode لتكون قيمة المتغيِّر في الحقيقة.
يحوي هذا الجدول رموز لجميع اللغات البشرية ابتداءً بالإنجليزية واللاتينية، ثم اللغات الأخرى كالعربية والعبرية والصينية واليابانية والكورية وبقية لغات العالم. بالإضافة إلى علامات الترقيم الخاصة بكل لغة، ورموز الأرقام. وفوق ذلك الرسوم التي تجدها في المحادثات ومواقع التواصل مثل: 🤗💡🔍📐 وما أشبهها.
لاحظ في النص البرمجي أن الدالة chr(i)
تحوِّل الرقم i
إلى رمز Unicode المقابل له. ونحن نستعملها في حلقة لكر الأرقام من 32 إلى 127 ونقسمها لمجموعات بحيث يكون كل سطر فيه مجموعة من الحروف:
for i in range(32, 127):
if i in [48, 58, 65, 91, 97, 123]:
print()
print(chr(i), end=' ')
! " # $ % & ' ( ) * + , - . /
0 1 2 3 4 5 6 7 8 9
: ; < = > ? @
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[ \ ] ^ _ `
a b c d e f g h i j k l m n o p q r s t u v w x y z
{ | } ~
ولو أردت العكس: أن تعرف ما هو الرقم للرمز، فإنك تستعمل دالة ord(c)
على النحو التالي:
print(ord("A"), ord("Z"), ord("a"), ord("z"), ord("0"), ord("9"))
print(ord("!"), ord("?"), ord("."), ord(","), ord(":"), ord(";"))
print(ord("ب"), ord("ي"), ord("ة"))
print(ord("😄"), ord("🚀"))
65 90 97 122 48 57
33 63 46 44 58 59
1576 1610 1577
128516 128640
لذلك فإن المقارنة بين السلاسل النصيَّة هي في الحقيقة مقارنة بأرقامها. فالحرف الصغير والحرف الكبير رقمان مختلفان:
print("A" == "a")
False
والحاصل في الواقع هو أن بايثون تقارنها بعد التحويل إلى الأرقام:
print(ord("A"), ord("a"))
print(ord("A") == ord("a"))
65 97
False
لذلك لا تعجب من أن الحرف الكبير أصغر من الصغير بهذا الاعتبار؛ وذلك: لأنه يأتي قبله في جدول الترميز العالمي، فرقمه أقل من رقم الصغير:
print("A" > "a")
False
والسبب في ابتدائنا من 32
هو أن الأرقام التي قبل ذلك تسىمى أحرف تحكم (Control Characters). بل إن رقم 32
نفسه هو حرف تحكُّم وظيفته تحريك رأس الكتابة بمقدار خطوة واحدة لتكوين مسافة.
print(chr(32) == " ")
print(ord(" ") == 32)
True
True
وهو من أحرف الفراغات (Whitespace Characters)، ويهمنا منها:
- المسافة (Space):
" "
ويعني الانتقال بمقدار خطوة واحدة لإحداث فراغ بين الكلمات - السطر (Line Feed):
"\n"
ويعني الانتقال للسطر التالي - البادئة (Tab):
"\t"
وهي التي تكون في بداية الفقرة، أو التي تكون بين الأعمدة في تنسيق الجداو. وقد نستعملها في كتابة بايثون للمحاذاة
ولاحظ أن الشرطة مع الحرف، يفسران كرقم واحد، وإنما وُضِعَت لها رموز لكثرة استعمالها، ويمكننا معرفة الأرقام الدالة على كل من هذه الرموز هكذا:
print(ord(" "), ord("\t"), ord("\n"))
32 9 10
ونحن في كل مرة نستعمل إجراء الطباعة: print()
فالقيمة الافتراضيَّة للمعطى end='\n'
، وهذا يعني أن الطباعة يتبعها علامة الانتقال لسطر جديد. فإذا أردت أن تكون الطباعة بلا سطرٍ جديد، بل تكون مثلاً، مسافة، فإننا نعيِّن قيمة المعطى عند الاستدعاء:
print("Hello", end=" ")
print("World")
Hello World
وبهذا تبيَّن لك كيف كانت جملة الكر تطبع الحروف بالنسق الذي أردناه لها.
إخراج النص
كثيرًا ما نحتاج لصياغة البيانات بقالب معيَّن، حتى يُرسَل ويفسَّر في الجهة المقابلة من برنامج آخر باعتبار نفس القالب، فتسهل قراءته بهذا الوجه.
ومن ذلك مثلاً رسائل البريد الإلكتروني حيث تكتب:
= """Assalamu Alaikum {candidate_name},
email_template
We're pleased to invite you for an interview for the {job_title} position.
Details:
- Date: {interview_date}
- Time: {interview_time} {time_zone}
- Method: {interview_method}
Please confirm your availability by {rsvp_date}.
We look forward to speaking with you, Insha'Allah!
With sincere regards,
The {company_name} Hiring Team
"""
وعملية الضرب تنتج تكرارًا للنص:
print('-' * 10)
print('=' * 10)
print('*' * 10)
----------
==========
**********
فنستطيع أن نطرِّز الرسالة به:
= "=" * 80
decor = decor + "\n" + email_template + "\n" + decor email_template
فهذا القالب يُمكن ملؤه بحسب المرسل والمرسل إليه، وذلك على النحو التالي:
= email_template.format(
filled_email ="Tech Solutions Inc.",
company_name="Software Engineer",
job_title="Adam Ben Saad",
candidate_name="July 15, 2045",
interview_date="10:00 AM",
interview_time="GMT+3",
time_zone="Online video call",
interview_method="July 10, 2045"
rsvp_date )
فإذا ما طبعنا هذا النص فسيظهر أن المتغير الآن يحتفظ بنسخة تمَّ تعبئة القالب فيه بالقيَم المعطاة:
print(filled_email)
================================================================================
Assalamu Alaikum Adam Ben Saad,
We're pleased to invite you for an interview for the Software Engineer position.
Details:
- Date: July 15, 2045
- Time: 10:00 AM GMT+3
- Method: Online video call
Please confirm your availability by July 10, 2045.
We look forward to speaking with you, Insha'Allah!
With sincere regards,
The Tech Solutions Inc. Hiring Team
================================================================================
كتابة النص
وتتم كتابة النص في الملفات باستعمال الإجراء write()
على النحو التالي:
file = open('email.txt', 'w')
file.write(filled_email)
file.close()
إدخال النص
والغالب أن وجود النص في البرنامج يكون ناتجًا عن قراءة ملف نصي بالإجراء open()
أي: في نفس المجلد الذي يوجد فيه البرنامج:
file = open('email.txt')
= file.read()
content file.close()
print(content)
================================================================================
Assalamu Alaikum Adam Ben Saad,
We're pleased to invite you for an interview for the Software Engineer position.
Details:
- Date: July 15, 2045
- Time: 10:00 AM GMT+3
- Method: Online video call
Please confirm your availability by July 10, 2045.
We look forward to speaking with you, Insha'Allah!
With sincere regards,
The Tech Solutions Inc. Hiring Team
================================================================================
وإذا كان الملف في مسارٍ آخر وأردنا قراءته، فوجب تعيين المسار بحسبه.
السياق
وهنا إشارة إلى أن عملية فتح الملف open()
يجب أن تتبع بعملية .close()
لإغلاقه. وهذا النمط يتكرر كثيرًا. ولذلك جعلت بايثون لهذا النمط كلمة with
بحيث يتم الإغلاق بعند انتهاء جسد السياق:
with open('email.txt') as file:
= file.read()
content print(content)
================================================================================
Assalamu Alaikum Adam Ben Saad,
We're pleased to invite you for an interview for the Software Engineer position.
Details:
- Date: July 15, 2045
- Time: 10:00 AM GMT+3
- Method: Online video call
Please confirm your availability by July 10, 2045.
We look forward to speaking with you, Insha'Allah!
With sincere regards,
The Tech Solutions Inc. Hiring Team
================================================================================
استخراج القيَم من النص
من أهم عمليات النصوص، استخراج المعلومات منها، فمثلاً، نريد استخراج:
- تاريخ المقابلة
- الوقت
- وطريقة المقابلة
وإذا لاحظنا هذه الرسالة، فإنها تعلمنا بأن أحد القوالب التي قد تستعمل هي:
- Date: {interview_date}
- Time: {interview_time} {time_zone}
- Method: {interview_method}
فلو سردنا سطور النص بحث عن هذه الكلمات، فإننا سنحقق مرادنا في استخراج القيمة، وهي الطرف الأيمن من ذلك النص. ويمكننا استعمال الدالة .splitlines()
لتحويل النص إلى قائمة من النصوص، بحيث يكون كل عنصرٍ فيها سطرًا من النص.
= content.splitlines()
lines print(len(lines))
19
ثم نكرها ونبحث عن الأنماط التي نريد، ولاحظ أننا نستعمل الإجراء .split()
لفصل النص إلى قائمة عند الفاصل الذي نريده، وهو النقطات الرأسيتان (:
) حتى يكون فيه جزءان، فنأخذ الجزء الثاني [1]
منها وهو القيمة. ونستعمل الإجراء .strip()
لإزالة الفراغات المتبقية من النص (المسافات الزوائد في البداية والنهاية).
for line in lines:
if "Date:" in line:
= line.split(":")[1].strip()
date elif "Time:" in line:
= line.split(":")[1].strip()
time elif "Method:" in line:
= line.split(":")[1].strip()
method
print("time =", time)
print("date =", date)
print("method =", method)
time = 10
date = July 15, 2045
method = Online video call
ويُراجع باب التعبير النمطي.
النص نوع ما لا نوع له
تذكر: النص نوع ما لا نوع له.
إذا أردت أن تحفظ نتائج المعالجة في ملف ليستقر في جهازك، فإنه يجب أن تحوِّل النص إلى قيمة عددية، وإلا ستفشل الكتابة كما في هذا المثال
= [1000, 500, 250, 750]
numbers = sum(numbers) total
with open('output.txt', 'w') as file:
file.write(total)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[234], line 2 1 with open('output.txt', 'w') as file: ----> 2 file.write(total) TypeError: write() argument must be str, not int
ويحصل تحويل العدد إلى نص باستعمال الدالة str(i)
:
= str(total)
content with open('output.txt', 'w') as file:
file.write(content)
فإن الملف وقت القراءة يُقرأ كنص؛ أي كسلسلة من الأحرف، ولذلك يجب تحويله إلى قيمة عددية، وإلا ستفشل العمليات إن ظننت أنها عددية، مثلما يفشل هذا المثال:
with open('output.txt') as file:
= file.read() content
print(content + 2500)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[237], line 1 ----> 1 print(content + 2500) TypeError: can only concatenate str (not "int") to str
ويحصل تحويل النص إلى عددية باستعمال الدالة int(s)
أو float(s)
:
= int(content)
total print(total + 2500)
5000
صياغة البيانات في قوالب جيسون (JSON)
وكما أن القالب المستعمل في الرسالة البريدية صالح لقراءة البشر، فكذلك البرمجيات تستعمل قوالب للتواصل فيما بينها، وكذلك تستعمل لتخزين المعلومات وقراءتها لاحقًا. ومن أشهرها قالب JSON لأنه بسيط جدًّا؛ فما هو إلا أزواج من المفاتيح والقيَم المقابلة لها (بعد النقطيتن الرأسيتين). وهي تشبه إلى حد كبير ما يسمى القاموس (dict
) في بايثون. وسنبيِّنُه إن شاء الله.
فمن المكتبة الأساسية نستورِد وحدة json
لصياغة البيانات من شكلها البايثوني إلى هذه الصيغة ثم كتابتها، أو لتحويلها من صيغتها تلك إلى القيمة البايثونية المقابلة لها.
import json
= [1200, -500, 300, -200, 450, -1000, 800]
transactions
with open('transactions.json', 'w') as file:
file) json.dump(transactions,
وعند القراءة نستعمل الإجراء json.load()
لتحويل تلك الصياغة النصيَّة إلى قائمة بايثونية:
with open('transactions.json') as file:
= json.load(file)
transactions print(sum(transactions))
1050
وأما إذا كانت قيَمًا متعددة، فالأفضل وضعها في قاموس (dict
) بحيث تكون أسماء المتغيرات مفاتيح (Keys)، وتكون قيمها قيَمًا لها (Values). ثم تُصاغ بصيغة JSON وتُحفظ في ملف:
= {
data "expenses": [1200, 300, 450, 800],
"revenues": [-500, -200, -1000],
}
with open('data.json', 'w') as file:
file) json.dump(data,
ثم يمكن قراءتُها:
with open('data.json') as file:
= json.load(file)
data print(data)
{'expenses': [1200, 300, 450, 800], 'revenues': [-500, -200, -1000]}
وكذلك يمكن استخراج القيمة منها، بالاسم الذي حُفِظَ به أولاً في القاموس:
= data["revenues"]
revenues print(sum(revenues) / len(revenues))
= data["expenses"]
expenses print(sum(expenses) / len(expenses))
-566.6666666666666
687.5
وقد تكون البيانات متضمنة بعضها في بعض، كتفضيلات المستخدم (user
):
= {
user "name": "Adam",
"language": "Arabic",
"phone": "966xxxxxxxxx",
"last_updated": "2021-09-01",
"age": 25,
"notifications": {
"email": "monthly",
"sms": "weekly",
"push": "daily"
},"emails": ["example1@domain.com", "example2@domain.com"]
}
فالمفتاح (Key) عادةً ما يكون نصًّا كما ترى. وأما القيمة (Value) فقد تكون نصًّا أو عددًا أو قائمة أو حتى قاموسًا!
وللوصول إلى قيمة مضمَّنة، قد تكتب:
= user['notifications']
a print(a)
{'email': 'monthly', 'sms': 'weekly', 'push': 'daily'}
= a['sms']
b print(b)
weekly
وذلك أن نوع قيمة المتغير a
هو قاموس:
type(a)
dict
أو تأتي بها مرة واحدة:
= user['notifications']['sms']
c print(c)
weekly
وكذا الأمر في الوصول للقائمة المضمَّنة (emails
) كما يلي:
= user['emails']
e print(e)
['example1@domain.com', 'example2@domain.com']
ثم الوصول إلى عنصرٍ من هذه القائمة:
print(e[0])
example1@domain.com
وذلك أن نوع قيمة المتغير c
هو قائمة:
type(e)
list
أو الوصول إليها مرة واحدة:
= user['emails'][0]
f print(f)
example1@domain.com
ومن الأنماط المستعملة بكثرة: استعمال القواميس كعناصر لقائمة. ولذلك وجب علينا التدرب على قراءتها.
فمثلاً هذه قائمة يحتوي كل عنصرٍ منها على قاموس لبيانات مُرَشَّح للتوظيف، ونريد أن نستخلص خبراتهم المهنية منها:
= [
data
{'name': 'Ahmad Hamada',
'experiences': [
{'company': 'Geo Space',
'start': '2038-01-01',
'end': '2039-01-01',
'role': 'Junior Software Engineer',
},
{'company': 'Space Roots',
'start': '2039-01-01',
'end': '2041-01-01',
'role': 'Senior Software Engineer',
},
],
},
{'name': 'Belal Banana',
'experiences': [
{'company': 'Banana Tech',
'start': '2041-01-01',
'end': '2042-01-01',
'role': 'Smoothie Operator',
},
{'company': 'BugSquash Labs',
'start': '2042-02-01',
'end': '2043-08-01',
'role': 'Code Pest Control Specialist',
},
{'company': 'Caffeinated Circuits Inc.',
'start': '2043-09-01',
'end': '2045-05-01',
'role': 'Espresso-Driven Engineer',
}
]
}, ]
ولنفترض أننا نريد آخر وظيفة شغلها المرشح الثاني، فإننا نكتبها في بايثون بهذا الشكل:
1]['experiences'][-1]['role'] data[
'Espresso-Driven Engineer'
وبايثون تقيِّمها (أي: تحسبها أو تفسِّرها) من اليسار إلى اليمين، على النحو التالي:
من المتغير data
أريد العنصر الثاني، ومنه أريد مقابل المفتاح experiences
، ومنه أريد العنصر الأخير، ومنه أريد مقابل المفتاح role
.
وقد يكون فصلها أوضَح هكذا:
- من المتغير
data
(هو قائمة) - منه: العنصر الثاني (
1
) (هو قاموس) - منه: مقابل المفتاح
experiences
(هو قائمة) - منه: العنصر الأخير (هو قاموس)
- منه: مقابل المفتاح
role
(هو نص)
فإذا أردت أن تقرأها بالعكس من اليمين إلى اليسار، فتقول:
- مقابل المفتاح
role
- في العنصر الأخير من
- مقابل المفتاح
experiences
- في العنصر الثاني (
1
) من - المتغير
data
وتقول أيضًا: أريد وظيفةَ آخر خبرات الثاني من المرشحين.
قراءة صيَغ الوصول هي من مهارات المبرمج الذي يحتاجها كثيرًا.
ويُحفظ هذا النص في ملف data.json
بالصيغة التالية:
with open('data.json', 'w') as file:
file) json.dump(data,
ثم يقرأ لاحقًا:
with open('data.json') as file:
= json.load(file)
data print(data[0]['name'])
Ahmad Hamada