ملحق K — النص

النص (str) صف من الأحرف. وهو تسلسل ثابت من أرقام الترميز العالمي (Unicode) التي هي رموز تتبع ترميزًا عالميًا يحوي جميع أحرف اللغات بالإضافة إلى علامات الترقيم والرسوم (مثل: 💡🔍📐) ونحوها.

إليك شجرة النص:

flowchart BT
    Container[<b>الحاوي</b><br>Container]
    Sized[<b>المحجَّم</b><br>Sized]
    Iterable[<b>القابل للكر</b><br>Iterable]
    Sequence[<b>التسلسل</b><br>Sequence]
    Collection[<b>الجمع</b><br>Collection]
    Collection --> Container
    Collection --> Sized
    Collection --> Iterable
    Sequence --> Collection

    str[<b>النص</b><br><code>str</code>]
    str --> Sequence
    bytes[<b>البايتات</b><br><code>bytes</code>]
    bytes --> Sequence

وإليك طرائق وإجراءات النص:

إجراءات مبنيَّة

الإجراء عمله
len(obj) -> int لمعرفة عدد العناصر.
obj: كائن قابل للعد مثل السلسلة النصية أو القائمة.
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) طباعة.
objects: الكائنات المراد طباعتها.
sep: الفاصل بين الكائنات (افتراضيًا مسافة).
end: النهاية (افتراضيًا سطر جديد).
file: وجهة الطباعة (افتراضيًا الشاشة).
flush: تنظيف المخزن المؤقت فورًا.
ord(c) -> int لمعرفة رمز Unicode لحرف.
c: حرف واحد كسلسلة نصية.
int(x=0, base=10) -> int تحويل سلسلة نصية إلى عدد صحيح.
x: القيمة المراد تحويلها.
base: النظام العددي (افتراضيًا عشري).
float(x) -> float تحويل سلسلة نصية إلى عدد عشري.
x: القيمة المراد تحويلها.

طرائق شائعة

الطريقة عمله
str.upper() -> str تحويل السلسلة النصية إلى أحرف كبيرة.
str.lower() -> str تحويل السلسلة النصية إلى أحرف صغيرة.
str.find(sub[, start[, end]]) -> int البحث عن موضع سلسلة نصية فرعية داخل سلسلة نصية.
sub: السلسلة النصية المراد البحث عنها.
start: موضع بداية البحث (اختياري).
end: موضع نهاية البحث (اختياري).
str.replace(old, new[, count]) -> str استبدال تكرارات سلسلة نصية فرعية داخل سلسلة نصية.
old: السلسلة النصية المراد استبدالها.
new: السلسلة النصية البديلة.
count: عدد مرات الاستبدال (اختياري).
str.split(sep=None, maxsplit=-1) -> list تقسيم السلسلة النصية إلى قائمة من السلاسل النصية الفرعية.
sep: المحدد (افتراضيًا المسافات البيضاء).
maxsplit: أقصى عدد للتقسيمات.
str.join(iterable) -> str دمج عناصر من iterable إلى سلسلة نصية باستخدام فاصل.
iterable: مجموعة من السلاسل النصية.
str.strip([chars]) -> str إزالة المسافات البيضاء من بداية ونهاية السلسلة النصية.
chars: الأحرف المراد إزالتها (اختياري).
str.startswith(prefix[, start[, end]]) -> bool التحقق مما إذا كانت السلسلة النصية تبدأ بمقدمة محددة.
prefix: المقدمة المراد التحقق منها.
start: موضع بداية البحث (اختياري).
end: موضع نهاية البحث (اختياري).
str.endswith(suffix[, start[, end]]) -> bool التحقق مما إذا كانت السلسلة النصية تنتهي بنهاية محددة.
suffix: النهاية المراد التحقق منها.
start: موضع بداية البحث (اختياري).
end: موضع نهاية البحث (اختياري).
str.count(sub[, start[, end]]) -> int عد عدد تكرارات سلسلة نصية فرعية داخل سلسلة نصية.
sub: السلسلة النصية المراد عدها.
start: موضع بداية البحث (اختياري).
end: موضع نهاية البحث (اختياري).
str.format(*args, **kwargs) -> str إنشاء سلسلة نصية منسقة باستخدام عناصر نائبة.
args: المتغيرات المراد تنسيقها بالترتيب.
kwargs: المتغيرات المراد تنسيقها بالاسم.

طرائق معروفة

الطريقة عمله
str.encode(encoding='utf-8', errors='strict') -> bytes ترميز السلسلة النصية باستخدام ترميز محدد.
encoding: نوع الترميز.
errors: كيفية معالجة الأخطاء.
str.casefold() -> str تحويل السلسلة النصية إلى نسخة casefold للمقارنات غير الحساسة لحالة الأحرف.
str.capitalize() -> str تحويل الحرف الأول من السلسلة النصية إلى حرف كبير.
str.center(width[, fillchar]) -> str توسيط السلسلة النصية داخل عرض حقل محدد.
width: العرض المطلوب.
fillchar: حرف التعبئة (اختياري).
str.translate(table) -> str استبدال الأحرف في السلسلة النصية باستخدام جدول ترجمة.
table: جدول الترجمة.
str.expandtabs(tabsize=8) -> str توسيع أحرف الجدولة في السلسلة النصية.
tabsize: حجم التبويب (افتراضيًا 8).
str.index(sub[, start[, end]]) -> int البحث عن موضع سلسلة نصية فرعية، وإثارة استثناء إذا لم يتم العثور عليها.
sub: السلسلة النصية المراد البحث عنها.
start: موضع بداية البحث (اختياري).
end: موضع نهاية البحث (اختياري).
str.rjust(width[, fillchar]) -> str محاذاة السلسلة النصية إلى اليمين.
width: العرض المطلوب.
fillchar: حرف التعبئة (اختياري).
str.ljust(width[, fillchar]) -> str محاذاة السلسلة النصية إلى اليسار.
width: العرض المطلوب.
fillchar: حرف التعبئة (اختياري).
str.swapcase() -> str تبديل حالة الأحرف في السلسلة النصية.
str.title() -> str تحويل السلسلة النصية إلى حالة عنوان.
str.zfill(width) -> str ملء السلسلة النصية بالأصفار على اليسار.
width: العرض المطلوب.
str.splitlines([keepends]) -> list تقسيم السلسلة النصية إلى قائمة من الأسطر.
keepends: الاحتفاظ بنهايات الأسطر (اختياري).
str.partition(sep) -> tuple تقسيم السلسلة النصية إلى ثلاثة أجزاء.
sep: الفاصل.

طرائق نادرة الاستعمال

الطريقة عمله
str.isalnum() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أحرف أبجدية رقمية.
str.isalpha() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أحرف أبجدية.
str.isdecimal() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أحرف عشرية.
str.isdigit() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أرقام.
str.isidentifier() -> bool التحقق مما إذا كانت السلسلة النصية معرف بايثون صالح.
str.islower() -> bool التحقق مما إذا كانت جميع الأحرف المكونة في السلسلة النصية صغيرة.
str.isnumeric() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أحرف رقمية.
str.isprintable() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من أحرف قابلة للطباعة.
str.isspace() -> bool التحقق مما إذا كانت السلسلة النصية تتكون فقط من مسافات بيضاء.
str.istitle() -> bool التحقق مما إذا كانت السلسلة النصية في حالة عنوان.
str.isupper() -> bool التحقق مما إذا كانت جميع الأحرف المكونة في السلسلة النصية كبيرة.

النص الطبيعي

النص الطبيعي (الحر أو البشري) هو ما لا يتبع هيكلاً أو قالبًا يحكم طريقة كتابته؛ وهو يطول ويقصر من الحرف الواحد إلى مجموعة الأحرف إلى الكلمة إلى الجملة وإلى أكبر من ذلك نحو:

  • محادثات وسائل التواصل الاجتماعي
  • رسائل البريد الإلكتروني
  • مقالات
  • كتب
  • موسوعات
  • صفحات الشبكة
  • مواقع الشبكة

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

النص المُقَوْلَب

النص المقولَب الذي يأخذ شكلاً محددًا؛ وله صيغ متعددة يُنشئ الناس منها قوالب للنص لتمثل مجموعات أو ارتباطات من البيانات، كالجداول ونحوها.

وصيغ القولبة كثيرة من أشهرها: XML, YAML, JSON, CSV.

مثال لقالب بصيغة YAML:

---
Name: Adam
Age: 25
City: Riyadh

مثال لقالب بصيغة JSON:

{
  "Name": "Adam",
  "Age": 25,
  "City": "Riyadh"
}

وهذا مثال لقالب بصيغة XML:

<person>
  <name>Adam</name>
  <age>25</age>
  <city>Riyadh</city>
</person>

والصيغة التي تستعمل كثيرًا لتمثيل الجداول (صفوف) هي صيغة CSV. فكل سطرٍ هو صفٌّ في الجدول، وأوَّلُ سطرٍ هو رأس الجدول عادةً. وذلك على النحو التالي:

Name,Age,City
Adam,25,Riyadh
Belal,30,Jeddah
Camal,35,Dammam

جرب أن تُنشئ ملفًّا نصيًّا وتكتب فيه ذلك وتحفظه بصيغة csv ثم سترى أنه يمكنك فتحه بمحرر جداول (:مثل إكسل - Excel).

وستأتي في فصل حفظ البيانات واسترجاعها إن شاء الله.

إنشاء النص

جملة إنشاء النص في بايثون تكون كالتالي:

  • بعلامة اقتباس مفردة: 'السلام عليكم!'
  • أو بعلامة اقتباس مزدوجة: "السلام عليكم!" وليس بينهما فرق.
  • أو بعلامة اقتاس مكررة ثلاثة مرات: """السلام عليكم!""" للنص الجاري على أكثر من سطر.

على سبيل المثال:

name = 'Adam'
address = "Makkah, Saudi Arabia"

تأمل إنشاء هذا النص الذي يبتدئ في السطر الأول ويمتد لأربعة أسطر:

message = """السلام عليكم ورحمة الله وبركاته,
أبشركم بأنكم قطعتم نصف المشوار.

شكرًا لكم.
أخوكم آدم.
"""
print(message)
السلام عليكم ورحمة الله وبركاته,
أبشركم بأنكم قطعتم نصف المشوار.

شكرًا لكم.
أخوكم آدم.

قراءة الملفات النصية

وكثيرًا ما يكون وجود النص في البرنامج ناتجًا عن قراءة ملف نصي بالإجراء open() وذلك بإضافة الحرف r ميشرًا إلى أن غرض الفتح للقراءة (ويجب أن يكون الملف موجودًا إلى جانب البرنامج - أي: في نفس المجلد الذي يوجد فيه البرنامج):

file = open('my_file.txt', 'r')
message = file.read()
file.close()
print(message)

وسيأتي مزيد بيان في باب الملفات.

الإشارة لجزء من النص

ولكون النص تسلسلاً ثابتًا فإنه يقبل جميع عمليات التسلسل السابق ذكرها (انظر باب المجموعة المرتبة).

s = 'Arabian'

first = s[0]
last = s[-1]
print(first + last)
An
 0   1   2   3   4   5   6   7
 +---+---+---+---+---+---+---+
 | A | r | a | b | i | a | n |
 +---+---+---+---+---+---+---+
-7  -6  -5  -4  -3  -2  -1

جرب

  • s[1:5]
  • s[1:5:2]
  • s[::2]
  • s[::-1]

لاحظ أن هذا يتحقق كما لو عرفنا صفًّا بالأحرف هذه نفسها:

s = ('A', 'r', 'a', 'b', 'i', 'a', 'n')
first = s[0]
last = s[-1]
print(first + last)
An

لكن الفرق أن النص نوعٌ له إجراءات / عمليات خاصة تتعلق بالنصوص.

النص ثابت لا يقبل التغير

فالنص تسلسل يشبه الصف في كوْنه ثابتًا (أي لا يقبل تعديل عنصر أو إضافته أو حذفه). فلو حاولت تغيير موضعٍ s[i] في النص أو قطعة s[i:j] فإنك ستواجه خطأ:

s = 'Arabian'
s[0] = 'a'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[5], line 2
      1 s = 'Arabian'
----> 2 s[0] = 'a'

TypeError: 'str' object does not support item assignment

لكننا لتعديل النص في الواقع نعيِّنُ إلى نفس المتغير نصًّا جديدًا منه:

s = 'Arabian'
s = 'a' + s[1:]
print(s)
arabian

لاحظ أننا أسندنا النص الجديد إلى المتغير s، وهذا يعني أننا لم نعدل النص الأصلي بل أنشأنا نصًا جديدًا.

العمليات الخاصة بالنصوص

الاستبدال

لاستبدال جزء من النص، نستخدم طريقة .replace():

s = 'Arabian'
s = s.replace('ian', 'y')
print(s)
Araby

وللإزالة من البداية والنهاية: .removeprefix() و .removesuffx()

print('Arabian'.removeprefix('Arab'))
print('Arabian'.removesuffix('ian'))
ian
Arab

إزالة المسافات

كما لدينا علميات .strip() لإزالة المسافات البيضاء من بداية ونهاية النص.

assert '  Arabian  '.strip() == 'Arabian'

البحث والمطابقة

وكذلك لدينا عمليات البحث:

  • التحقق من البدء والنهاية وما بينهما: .startswith(prefix) و .endswith(suffix) أو sub in string وهي أعم.
  • لمعرفة موضع أول ظهور لسلسلة معيَّنة من الأحرف داخل النص .find()
s = 'Arabian'
assert s.startswith('A')
assert not s.startswith('a')
assert s.endswith('n')
assert 'rabia' in s
assert s.find('a') == 2

الفصل والوصل

  • فصل النص لقائمة بناءً على جزء فاصل: list.split(seperator)
  • وصل عناصر القائمة في نص مفصول بجزء فاصل: seperator.join(list)

أولاً: الفصل: مثاله قراءة سطر في ملف csv إذْ القيم مفصولة بعلامة الفاصلة ,:

csv = "Adam,25,Riyadh"
assert csv.split(',') == ['Adam', '25', 'Riyadh']

ولقراءة الملف كاملاً نكرر:

  • القيم في السطر الواحد مفصولة بعلامة الفاصلة ,
  • والصفوف مفصولة بعلامة السطر الجديد \n

لذلك سنستعمل الاثنين هنا:

csv = """name,age,city
Adam,25,Riyadh
Belal,30,Jeddah
Camal,35,Dammam"""

data = []
for line in csv.split(sep='\n'):
  row = line.split(sep=',')
  data.append(row)
print(data)
[['name', 'age', 'city'], ['Adam', '25', 'Riyadh'], ['Belal', '30', 'Jeddah'], ['Camal', '35', 'Dammam']]

ثانيًا: الوَصل: مثاله أننا نحول القائمة إلى نص مفصول بعلامة الفاصلة , وذلك ليكون سطرًا في ملف csv:

data = ['Adam', '25', 'Riyadh']
seperator = ','
csv = seperator.join(data)
assert csv == 'Adam,25,Riyadh'

عمليات إنجليزية

وهنا عمليات خاصة بالنصوص الإنجليزية:

  • upper() نسخة ذات حروف كبيرة.
  • lower() نسخة ذات حروف صغيرة.
  • capitalize() نسخة ذات حرف أول كبير من كل كلمة.
  • title() نسخة ذات حالة عنوان.
name = 'Adam ibraheeM'
print(name.upper())
print(name.lower())
print(name.capitalize())
print(name.title())
ADAM IBRAHEEM
adam ibraheem
Adam ibraheem
Adam Ibraheem

وانظر مستندات النصوص لمعرفة كافة الإجراءات الممكنة على النصوص.

تفسير الأرقام

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

  • int(x) لتحويل نص إلى عدد صحيح.
  • float(x) لتحويل نص إلى عدد عشري.

لاحظ ناتج عملية جمع رقمين مكتوبين كنصوص:

x = '20'
y = '40'
print(x + y)
2040

الواجب تحويلهما إلى أعداد أولاًً:

x2 = float(x)
y2 = float(y)
print(x2 + y2)
60.0

إخراج النص

نستعرض ثلاث طرق لدمج النصوص في بايثون:

  1. الأولى: تحويل الشيء إلى نص قبل دمجه مع النص، باستعمال str(x)
  2. الثانية: استعمال فراغات بالقوسين المعكوفين {} مع الإجراء .format() لاستبدالها.
  3. الثالثة: استعمال f-string بأن تضع الحرف f قبل علامة التنصيص الأولى، ليقبل النص وضع القيم مباشرة داخل الأقواس المعكوفة {}. وهي الطريقة التي نفضلها.
name = "John"
lvl = 300

s1 = "I am " + name + " and I want to reach level " + str(lvl) # + operator
s2 = "I am {} and I want to reach level {}".format(name, lvl)  # .format() method
s3 = f"I am {name} and I want to reach level {lvl}"            # f-strings
assert s1 == s2 == s3
print(s1)
I am John and I want to reach level 300

يقبل النص التكرار بعلامة *:

print('-' * 10)
print('*' * 10)
print('=' * 10)
----------
**********
==========

المحاذاة والحشو

name = 'Adam'
print(name.ljust(15)) 
print(name.center(15))
Adam           
      Adam     

طريقة ممتعة لتزيين سلسلة نصية باستخدام الإجراء center:

print('*' * 20)
print('Adam'.center(20, "*"))
print('*' * 20)
********************
********Adam********
********************

تنسيق الأرقام

محاذاة الأرقام

print(f'{100:10}')
print(f'{1000:10}')
print(f'{10000:10}')
       100
      1000
     10000

يمكننا أيضًا جعل كل من العدد n والتعبئة p متغيرات:

n = 100
p = 5
print(f'{n:{p}}')
  100

بشكل افتراضي، يتم محاذاة الرقم إلى اليمين. يمكننا محاذاته إلى اليسار بإضافة <. لاحظ الفرق بين التعبيرين أدناه:

print(f'{n:>{p}}')
print(f'{n:<{p}}')
  100
100  

فاصل الآلاف

big_num = 10000
print(f'{big_num}')
print(f'{big_num:,}')
10000
10,000

الترميز العلمي

small_num = 0.00001
print(f"{small_num:.2e}")
1.00e-05

الأعداد العشرية

num = 10.5689
print(f'{num}')
print(f'{num:.4f}')
print(f'{num:.2f}')
print(f'{num:.0f}')
10.5689
10.5689
10.57
11

مثال: تنسيق الكم المالي

price_dollars = 2978.95
price_riyals = price_dollars * 3.75
print(f"${price_dollars:,.2f} = {price_riyals:,.2f} SAR")
$2,978.95 = 11,171.06 SAR