السلاسل النصية المتقدمة
الدرس 06 غطى الأساسيات. الآن دعنا نتعمق أكثر. ماذا يعني عدم القابلية للتعديل حقاً؟ ما هي عمليات السلاسل النصية الأقل شهرة لكنها قوية؟ كيف تُخزَن الأحرف الصينية في الحواسيب؟ بعد هذا الدرس، سيكون فهمك للسلاسل النصية في مستوى جديد.
1. عدم قابلية تغيير السلاسل النصية
ذكرناها بإيجاز في الأسئلة الشائعة للدرس 06 — بمجرد إنشائها، لا يمكن تعديل السلاسل النصية. دعنا نرى ماذا يعني هذا بشكل ملموس.
text = "Hello"
# ❌ لا يمكن التعديل هكذا — هذا سيُسبب خطأ
# text[0] = "h" # TypeError: 'str' object does not support item assignment
لتغيير الحرف الأول إلى حرف صغير، أنشئ سلسلة جديدة:
text = "Hello"
new_text = "h" + text[1:] # أنشئ سلسلة جديدة
print(new_text) # hello
print(text) # Hello — الأصلي لم يتغير
فوائد عدم القابلية للتعديل
# مراجع مشتركة آمنة
a = "Python"
b = a # b و a يشيران إلى نفس السلسلة
# بغض النظر عما تفعله بـ a، b لا يتأثر
a = a.upper()
print(a) # PYTHON
print(b) # Python — b لا يزال يحتفظ بالقيمة الأصلية
تكلفة عدم القابلية للتعديل
كل "تعديل" يُنشئ كائناً جديداً. دمج العديد من السلاسل النصية في حلقة بطيء:
# ❌ غير موصى به: دمج السلاسل النصية في حلقة
result = ""
for i in range(10000):
result += str(i) + ","
# هذا يُنشئ 10000 كائن سلسلة مؤقت
# ✅ موصى به: اجمع في قائمة، ثم استخدم join()
parts = []
for i in range(10000):
parts.append(str(i))
result = ",".join(parts)
+= جيد تماماً.
مثال: محاكاة طرق السلاسل النصية (الصعوبة ⭐⭐)
# نفذ دالة "capitalize"
def my_capitalize(text):
if not text:
return text
first = text[0].upper()
rest = text[1:].lower()
return first + rest
print(my_capitalize("hello WORLD")) # Hello world
print(my_capitalize("python")) # Python
# نفذ دالة "replace"
def my_replace(text, old, new):
parts = text.split(old) # قسّم بـ old، احصل على الأجزاء
return new.join(parts) # أعد الدمج بـ new
print(my_replace("one, two, one", "one", "1")) # 1, two, 1
2. طرق الاستعلام عن السلاسل النصية
إلى جانب find() و count() من الدرس 06، إليك المزيد من الطرق المفيدة.
startswith() و endswith()
# تحقق من بداية/نهاية السلسلة
url = "https://www.example.com"
print(url.startswith("https")) # True — بروتوكول HTTPS
print(url.endswith(".com")) # True — نطاق .com
print(url.endswith(".org")) # False
# يمكن التحقق من عدة في وقت واحد
print(url.endswith((".com", ".org", ".cn"))) # True — يطابق .com
الاستخدامات العملية
# التحقق من امتداد الملف
filename = "report.pdf"
if filename.endswith((".pdf", ".doc", ".docx")):
print("Document file")
elif filename.endswith((".jpg", ".png", ".gif")):
print("Image file")
elif filename.endswith((".py", ".js", ".html")):
print("Code file")
else:
print("Other file")
# التحقق من بروتوكول URL
link = "ftp://files.example.com"
if link.startswith(("http://", "https://")):
print("Web link")
elif link.startswith("ftp://"):
print("FTP link")
المخرجات:
Document file
FTP link
removeprefix() و removesuffix() (بايثون 3.9+)
أُضيفتا في بايثون 3.9 لإزالة البادئات أو اللواحق بشكل نظيف:
url = "https://www.example.com"
# أزل بادئة البروتوكول
domain = url.removeprefix("https://")
print(domain) # www.example.com
# أزل لاحقة النطاق
site_name = url.removesuffix(".com")
print(site_name) # https://www.example
# إذا كانت السلسلة لا تبدأ/تنتهي بالنص المحدد، أعدها كما هي
print(url.removeprefix("ftp://")) # https://www.example.com (لم تتغير)
url[len("https://"):] أو url.split("://", 1)[1]. removeprefix() و removesuffix() تجعل هذا أكثر بديهية.
3. استخدام split() المتقدم
الدرس 06 غطى التقسيم الأساسي. دعنا نستكشف المتغيرات الأكثر قوة.
تحديد عدد التقسيمات
# حدد التقسيمات بمعامل maxsplit
text = "one,two,three,four,five"
print(text.split(",", 1)) # ['one', 'two,three,four,five']
print(text.split(",", 2)) # ['one', 'two', 'three,four,five']
print(text.split(",", 3)) # ['one', 'two', 'three', 'four,five']
rsplit(): تقسيم من اليمين إلى اليسار
text = "one,two,three,four,five"
print(text.rsplit(",", 1)) # ['one,two,three,four', 'five']
print(text.rsplit(",", 2)) # ['one,two,three', 'four', 'five']
splitlines(): تقسيم بأسطر
multiline = """First line
Second line
Third line"""
lines = multiline.splitlines()
print(lines) # ['First line', 'Second line', 'Third line']
# احتفظ بفواصل الأسطر
lines = multiline.splitlines(True)
print(lines) # ['First line\n', 'Second line\n', 'Third line']
partition() و rpartition()
partition() لا تقسم فقط بل تحتفظ أيضاً بموضع الفاصل:
text = "user@example.com"
# قسّم بـ @ إلى ثلاثة أجزاء: (يسار، فاصل، يمين)
parts = text.partition("@")
print(parts) # ('user', '@', 'example.com')
# إذا لم يُعثر على الفاصل، يُرجع (original_string, '', '')
print("hello".partition("@")) # ('hello', '', '')
# rpartition يبحث من اليمين
path = "/home/user/documents/file.txt"
last_slash = path.rpartition("/")
print(last_slash) # ('/home/user/documents', '/', 'file.txt')
partition() أفضل من split() لسيناريوهات "الاستخراج" — تخبرك بالضبط ما هو كل جزء من الأجزاء الثلاثة.
مثال: تحليل URL (الصعوبة ⭐⭐)
# حلل URL باستخدام partition
url = "https://www.example.com:8080/path/page.html?name=test&page=1"
# استخرج البروتوكول
proto, _, rest = url.partition("://")
print(f"Protocol: {proto}") # https
# استخرج المضيف والمسار
host_part, _, path_and_query = rest.partition("/")
print(f"Host: {host_part}") # www.example.com:8080
# استخرج المنفذ
host, _, port = host_part.partition(":")
print(f"Domain: {host}") # www.example.com
print(f"Port: {port}") # 8080
# استخرج المسار والاستعلام
path, _, query = path_and_query.partition("?")
print(f"Path: /{path}") # /path/page.html
print(f"Query: {query}") # name=test&page=1
المخرجات:
Protocol: https
Host: www.example.com:8080
Domain: www.example.com
Port: 8080
Path: /path/page.html
Query: name=test&page=1
4. المحاذاة والحشو في السلاسل النصية
توفر بايثون طرقاً لمحاذاة السلاسل النصية ضمن عرض محدد — يسار، يمين، أو وسط.
text = "Python"
print(text.ljust(10)) # 'Python ' (محاذاة لليسار، حشو بالمسافات على اليمين)
print(text.rjust(10)) # ' Python' (محاذاة لليمين، حشو على اليسار)
print(text.center(10)) # ' Python ' (توسيط، حشو على كلا الجانبين)
# يمكن تحديد حرف الحشو
print(text.ljust(10, "-")) # 'Python----'
print(text.rjust(10, "-")) # '----Python'
print(text.center(10, "-")) # '--Python--'
zfill(): حشو بالأصفار
شائع الاستخدام لترقيم المعرفات:
print("42".zfill(5)) # 00042
print("-42".zfill(5)) # -0042 — الإشارة السالبة تأتي أولاً
print("3.14".zfill(8)) # 00003.14
# أنشئ معرفات مكونة من 3 أرقام
for i in range(1, 11):
filename = f"photo_{str(i).zfill(3)}.jpg"
print(filename, end=" ")
المخرجات:
photo_001.jpg photo_002.jpg photo_003.jpg photo_004.jpg photo_005.jpg
photo_006.jpg photo_007.jpg photo_008.jpg photo_009.jpg photo_010.jpg
مثال: إنشاء تقرير محاذى (الصعوبة ⭐⭐)
# محاذاة جدول باستخدام rjust و ljust
items = [
("Apple", 5.0, 3),
("Banana", 3.5, 5),
("Milk", 12.0, 2),
]
# العناوين
header_name = "Item".ljust(8)
header_price = "Price".rjust(6)
header_qty = "Qty".rjust(4)
header_total = "Total".rjust(6)
print(f"{header_name}{header_price}{header_qty}{header_total}")
print("-" * 28)
# صفوف البيانات
for name, price, qty in items:
total = price * qty
name_col = name.ljust(8)
price_col = f"{price:.1f}".rjust(6)
qty_col = str(qty).rjust(4)
total_col = f"{total:.1f}".rjust(6)
print(f"{name_col}{price_col}{qty_col}{total_col}")
المخرجات:
Item Price Qty Total
────────────────────────────
Apple 5.0 3 15.0
Banana 3.5 5 17.5
Milk 12.0 2 24.0
5. ترميز الأحرف: ord() و chr()
كل حرف في الحاسوب يقابله رقم (نقطة ترميز). ord() تحصل على الترميز الرقمي للحرف؛ chr() تفعل العكس.
# عرض نقاط ترميز Unicode
print(ord("A")) # 65
print(ord("中")) # 20013
print(ord("❤")) # 10084
# احصل على الحرف من نقطة الترميز
print(chr(65)) # A
print(chr(20013)) # 中
print(chr(10084)) # ❤
# أنشئ جميع الأحرف الكبيرة
for code in range(ord("A"), ord("Z") + 1):
print(chr(code), end=" ")
# يُخرج: 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
ما هو Unicode
ببساطة، Unicode هو معيار ترميز أحرف عالمي — يعين رقماً فريداً لكل حرف من كل نظام كتابة في العالم (صيني، إنجليزي، عربي، ياباني، إلخ.). بايثون 3 تخزن جميع السلاسل النصية داخلياً كـ Unicode، لذا فهي تدعم اللغات المتعددة أصلاً.
# سلاسل بايثون 3 تدعم Unicode أصلاً
text = "Hello, 你好, مرحبا, こんにちは"
print(text) # كلها تُعرض بشكل صحيح
print(len(text)) # عدد الأحرف
# يمكنك أيضاً تمثيل الأحرف بهروب \u
print("\u0048") # H
print("\u4e2d\u6587") # 中文 (صينية)
str (بايتات) و unicode (نص)، مما تسبب في أخطاء ترميز متكررة. بايثون 3 وحدتها — جميع السلاسل النصية هي Unicode، تعامل الصينية والإنجليزية بالتساوي. هذا أحد أفضل قرارات تصميم بايثون 3.
الترميز وفك الترميز
بينما تستخدم بايثون Unicode داخلياً، يستخدم تخزين الملفات ونقل الشبكة البايتات. لذا تحتاج إلى الترميز (encode) و فك الترميز (decode):
text = "Hello, 世界"
# الترميز: سلسلة ← بايتات
utf8_bytes = text.encode("utf-8")
print(utf8_bytes) # b'\xe4\xbd\xa0\xe5\xa5\xbd...'
print(len(utf8_bytes)) # عدد البايتات في UTF-8
gbk_bytes = text.encode("gbk")
print(len(gbk_bytes)) # عدد البايتات في GBK
# فك الترميز: بايتات ← سلسلة
decoded = utf8_bytes.decode("utf-8")
print(decoded) # Hello, 世界
# إذا لم يتطابق الترميز/فك الترميز، يحدث خطأ
# gbk_bytes.decode("utf-8") # UnicodeDecodeError!
encoding="utf-8" عند فتح الملف وسيعمل.
6. تكميل تنسيق السلاسل النصية
إلى جانب f-strings، لدى بايثون طريقتان قديمتان للتنسيق. بينما f-strings هي المعيار الحديث، قد تصادف هذه في الكود القديم.
طريقة format() (بايثون 3.0+)
# حسب الموضع
print("My name is {}, I'm {} years old.".format("Alice", 18))
# حسب الاسم
print("My name is {name}, I'm {age} years old.".format(name="Bob", age=22))
# تنسيق الأرقام
print("Pi: {:.3f}".format(3.14159))
تنسيق % (عصر بايثون 2)
# قد ترى هذا في الكود القديم
print("My name is %s, I'm %d years old." % ("Alice", 25))
print("Pi: %.2f" % 3.14159)
format() لا تزال مفيدة لـ "التنسيق المؤجل" (حدد قالباً الآن، املأ القيم لاحقاً). تنسيق % فقط للتعرف عليه عندما تصادفه؛ لا تستخدمه في الكود الجديد.
حالات الاستخدام الشائعة
- إعادة تسمية الملفات دفعة واحدة: استخدم
endswith()لفلترة أنواع الملفات،zfill()للترقيم الموحد. - تحليل URL: استخدم
partition()وremoveprefix()لاستخراج البروتوكول والنطاق والمسار. - معالجة بيانات CSV: استخدم
split()معmaxsplitللتحكم في عدد التقسيمات، لتجنب تداخل الفواصل داخل الحقول. - تنسيق السجلات: استخدم
center()وljust()لإخراج سجلات محاذية. - تحويل الترميز: تعامل مع ملفات نصية من مصادر مختلفة باستخدام
encode()وdecode(). - التحقق من صحة البيانات: استخدم
startswith()وendswith()للتحقق من التنسيقات (البريد الإلكتروني، الهاتف، الروابط).
❓ أسئلة شائعة
bytes. هذا يجعل عمليات السلاسل النصية بديهية — len("中") هو دائماً 1 على أي نظام. المقايضة هي أنك تحتاج إلى ترميز/فك ترميز يدوي لملف أو إدخال/إخراج الشبكة.
⚠️ س:متى يجب استخدام split() مقابل partition()؟ ج:للتقسيم البسيط، استخدم split(). عندما تحتاج إلى الفاصل نفسه، استخدم partition(). partition() دائماً يُرجع ثلاثة عناصر (يسار، فاصل، يمين)، مفيد لتفكيك الصف: left, sep, right = text.partition(":"). split() يُرجع قائمة بطول متغير. إذا ظهر المحدد مرة واحدة فقط وتحتاج محتوى على كلا الجانبين، partition() هو الخيار الأفضل.
س:ما الفرق بين ljust() و {value:10} في f-string؟ ج:ينتجان نفس النتيجة — text.ljust(10) يعادل f"{text:<10}". نسخة f-string أكثر مرونة (تجمع المحاذاة مع تنسيق الأرقام)، بينما استدعاء الطريقة أكثر بديهية. إنها مسألة تفضيل — اختر واحدة والتزم بها. رموز المحاذاة في f-strings: < يسار، > يمين، ^ وسط: f"{text:^10}".
س:ما الفرق بين UTF-8 و GBK؟ لماذا يحفظ ملف بايثون الخاص بي أحياناً بأحرف مشوشة؟ ج:UTF-8 هو المعيار الدولي — 1-4 بايتات لكل حرف، عالمي. GBK معيار صيني (صينية مبسطة) — 1-2 بايتات لكل حرف، يدعم فقط الصينية والإنجليزية. إذا تم حفظ ملف بايثون الخاص بك كـ GBK ولكنه يحتوي على نص ياباني أو عربي، سيُحدث خطأ أو يعرض نصاً مشوشاً. الحل: احفظ جميع ملفات بايثون كـ UTF-8 — بايثون 3 تتعامل مع UTF-8 كافتراضي.📖 ملخص
- السلاسل النصية غير قابلة للتعديل: كل "تعديل" يُنشئ كائناً جديداً؛ استخدم
join()بدلاً من+=للدمج الكبير - طرق الاستعلام:
startswith()/endswith()تتحقق من البداية/النهاية؛ تدعم وسيطات صف لفحوصات متعددة - بايثون 3.9+:
removeprefix()/removesuffix()لإزالة نظيفة للبادئة/اللاحقة - التقسيم المتقدم:
split(maxsplit=N)تحديد،rsplit()من اليمين،splitlines()بأسطر partition()يحتفظ بمعلومات الفاصل، مثالي للتحليل المنظم- المحاذاة:
ljust()/rjust()/center()/zfill()؛ أحرف حشو مخصصة مدعومة ord()يحصل على نقطة ترميز Unicode للحرف؛chr()يحولها مرة أخرى- سلاسل بايثون 3 تستخدم Unicode داخلياً؛ ترميز/فك ترميز باستخدام
encode()/decode()؛ يُوصى بـ UTF-8
📝 تمارين
-
أساسي (الصعوبة ⭐): بالنظر إلى
files = ["report.pdf", "photo.jpg", "script.py", "notes.txt", "index.html"]، استخدمendswith()لفلترة:- جميع ملفات الصور (.jpg, .png, .gif)
- جميع ملفات المستندات (.pdf, .doc, .docx, .txt)
- جميع ملفات الكود (.py, .js, .html, .css)
-
متوسط (الصعوبة ⭐⭐): اكتب "أداة إعادة تسمية ملفات دفعة واحدة." بالنظر إلى
photos = ["IMG_1.jpg", "IMG_2.jpg", ..., "IMG_12.jpg"]، أعد تسميتها إلىphoto_001.jpg,photo_002.jpg, ...,photo_012.jpg. تلميح: استخدمpartition()لتقسيم اسم الملف والامتداد؛ استخدمzfill()للحشو بالأصفار. -
تحدي (الصعوبة ⭐⭐⭐): اكتب "محرك قوالب بسيط." بالنظر إلى سلسلة قالب
templateوقاموسdata، استبدل عناصر نائبة{variable}بالقيم الفعلية.PYTHONtemplate = "Dear {name}, your order {order_id} has been {status}. Estimated delivery: {delivery_time}." data = { "name": "Alice", "order_id": "20260623001", "status": "shipped", "delivery_time": "3 business days" }متطلب إضافي: إذا كان متغير القالب غير موجود في البيانات، اترك
{variable}كما هو (لا تستبدل). تلميح: كرّر على أزواج المفتاح-القيمة في البيانات، واستدعreplace()على القالب لكل منها.



