العمليّات على الجمل النصية
كل ما يصل إلى البرنامج من العالم الخارجي يصل نصا: ما يكتبه المستخدم، وما يرسله أردوينو عبر المنفذ، وما يُقرأ من ملف. وحتى القراءة 12.42 التي تبدو عددا هي في البداية حروف متتالية. في هذا الفصل ننظر إلى داخل الجملة النصية: نصل إلى كل حرف فيها، نقصّ منها ما نريد، نبحث فيها ونقسمها، وننهي الفصل ببرنامج يفكّ سطرا كاملا من حسّاسات المحطة.
- نعرّف جملة نصية، ونعرف الجملة الفارغة.
- نصل إلى كل حرف بالمؤشّر، بالعد من البداية ومن النهاية.
- نقصّ جزءا من جملة نصية، ونحوّل القيم إليها.
- نستعمل المعاملات
+و*وinعلى الجمل النصية. - نستعمل عمليّات الفئة
str، ونعرف ماذا يحدث عندما لا تجد ما تبحث عنه. - نفكّ سطر بيانات ونفحص صحته قبل أن نحسب به.
5.1 تعريف الجمل النصية واستعمالها، والجملة النصية الفارغة – הגדרה ושימוש בסיסי במחרוזות, הגדרת המחרוזת הריקה
str."A0" جملة فيها حرفان، والمسافة حرف كغيرها.نكتب الجملة بين علامتي اقتباس مزدوجتين أو مفردتين، ولا فرق بينهما، وإذا احتوت على علامة اقتباس أحطناها بالنوع الآخر. والدالة len تعطي عدد الحروف. سنعود إليها في البند 5.7، لكننا سنحتاج إليها في كل بند:
device = "voltage sensor"
port = 'A0'
note = "the 'red' wire"
print(device)
print(note)
print(type(port))
print(len(device))
print(len(port))
print("line 1\nline 2")
print(len("A\nB"))voltage sensor the 'red' wire <class 'str'> 14 2 line 1 line 2 3
عدّت len المسافة بين الكلمتين حرفا. والرمز \n يعني الانتقال إلى سطر جديد: نكتبه في الشيفرة بإشارتين، لكنه حرف واحد في الجملة، ولذلك طول "A\nB" هو 3.
"" وطولها صفر. وهي تختلف عن " "، فتلك جملة فيها حرف واحد هو المسافة.empty = "" space = " " print(len(empty), len(space)) print(empty == space) print(bool(empty), bool(space))
0 1 False False True
input جملة فارغة. نكتب مصفاة مُدخَلات كما تعلمنا في الفصل 4، ولا نقبل الاسم الفارغ.name = input("Sensor name: ")
while name == "":
print("Name is empty")
name = input("Sensor name: ")
print("Saved:", name)Sensor name: Name is empty Sensor name: TMP36 Saved: TMP36
"9" أكبر من "12" كجملتين، لأن الحرف 9 يأتي بعد الحرف 1. والحرف الكبير يختلف عن الصغير، والجملة النصية لا تساوي عددا أبدا:print("on" == "ON")
print("12" == 12)
print("9" > "12")
print(9 > 12)False False True False
a = ""
b = "A0 1"
print(len(a) + len(b))
print(a == " ")
print(b == "a0 1")
print("5" > "10")5.2 البنية المتسلسلة للحروف والوصول إلى حرف بالمؤشّر – המבנה הסידורי של תווים במחרוזת, גישה לתו באמצעות אינדקס
الحروف في الجملة النصية مرتبة في صف، ولكل حرف مكان ثابت. الرقم الذي يدل على المكان هو المؤشّر (אינדקס)، ونكتبه بين قوسين مربعين بعد اسم الجملة: s[i].
"LED 1 ON": الموجبة فوق الحروف تبدأ من 0، والسالبة تحتها تعدّ من النهاية وتبدأ من −1- الحرف الأول مؤشّره
0وليس1. - الحرف الأخير مؤشّره
len(s) - 1، أو باختصار-1. - المؤشّر السالب
-kيعني الحرف الذي مؤشّرهlen(s) - k.
cmd = "LED 1 ON" print(cmd[0], cmd[4], cmd[7]) print(cmd[-1], cmd[-2]) print(cmd[len(cmd) - 1]) print(type(cmd[0]))
L 1 N N O N <class 'str'>
السطر الأخير يبيّن أن الحرف الواحد في بايثون هو أيضا من النوع str: جملة نصية طولها واحد. لا يوجد نوع خاص بالحرف.
"R12" و"C7". نقرأ عنوانا ونطبع نوع القطعة.addr = input("Address: ")
if addr[0] == "R":
print("Resistor")
elif addr[0] == "C":
print("Capacitor")
else:
print("Other part")Address: C7 Capacitor
2. المؤشّر 3 خارج الجملة، وهو يوقف البرنامج بالخطأ IndexError. والجملة الفارغة ليس فيها حرف أول أصلا، ولذلك عندما لا نعرف ما الذي وصل نكتب addr != "" and addr[0] == "R"، معتمدين على اختصار الطريق من الفصل 3.addr = "R12" addr[0] = "C"
TypeError: 'str' object does not support item assignment
المرور على الحروف:
المؤشّر عدد، ولذلك نستطيع أن نجعله عدّادا في حلقة while من الفصل 4. يبدأ من 0، ويزداد في كل دورة، وتتوقف الحلقة عندما يصل إلى طول الجملة:
tag = "R12"
i = 0
while i < len(tag):
print(i, tag[i])
i += 1| الأمر | tag | i | i < len(tag) | المُخرَج |
|---|---|---|---|---|
| tag = "R12" | 'R12' | |||
| i = 0 | 0 | |||
| while i < len(tag): | True | |||
| print(i, tag[i]) | 0 R | |||
| i += 1 | 1 | |||
| while i < len(tag): | True | |||
| print(i, tag[i]) | 1 1 | |||
| i += 1 | 2 | |||
| while i < len(tag): | True | |||
| print(i, tag[i]) | 2 2 | |||
| i += 1 | 3 | |||
| while i < len(tag): | False |
الشرط هو i < len(tag) وليس i <= len(tag): عندما يصل i إلى 3 تكون الجملة قد انتهت، ولا نريد أن ندخل الدورة.
cmd = "LED 1 ON"cmd[1]cmd[-1]cmd[5] == " "len(cmd)cmd[len(cmd) - 3]cmd[-8]E 2 N 3 True 4 8 5 6 L text = "A0=512"
i = len(text) - 1
while i >= 3:
print(text[i], end="")
i -= 1
print()
print(text[-6])5.3 قصّ الجملة النصية – חיתוך מחרוזת
المؤشّر يعطي حرفا واحدا. أما القصّ (חיתוך) فيعطي قطعة كاملة: جملة نصية جديدة، والأصل يبقى كما هو.
startهو مؤشّر الحرف الأول في القطعة. إذا حذفناه بدأنا من أول الجملة.stopهو المؤشّر الذي نتوقف قبله: الحرف الذي عنده لا يدخل في القطعة. إذا حذفناه وصلنا إلى آخر الجملة.stepهو الخطوة (צעד): كل كم حرفا نأخذ حرفا. إذا حذفناها كانت 1.
stop الذي لا يدخلcmd = "LED 1 ON" print(cmd[0:3]) print(cmd[4:5]) print(cmd[:3]) print(cmd[6:]) print(cmd[-2:]) print(cmd[:-3])
LED 1 LED ON ON LED 1
عدد الحروف في القطعة هو stop - start، وهذا يسهّل الفحص: cmd[0:3] فيها ثلاثة حروف. والمؤشّرات السالبة تعمل هنا أيضا، فالسطر الأخير يعني: كل الجملة ما عدا آخر ثلاثة حروف. والخطوة السالبة تعني أن نسير إلى الخلف، وهذا مفيد عندما يصل الإطار والخانة الأقل وزنا في أوله:
bits = "10110010" print(bits[:4]) print(bits[::2]) print(bits[::-1])
1011 1101 01001101
stop لا يدخل، فنكتب cmd[0:2] ونحن نريد الحروف الثلاثة الأولى. والقصّ، بخلاف المؤشّر، لا يوقف البرنامج عندما تقع الحدود خارج الجملة: يأخذ ما هو موجود فقط، وإذا لم يوجد شيء أعطى جملة فارغة. فالتعبير "R12"[1:100] يعطي 12، ولا تصلنا أي رسالة تكشف الخطأ.والقصّ يكمل ما بدأناه في البند 5.2: في العنوان addr = "R12" يكون addr[0] هو نوع القطعة، وaddr[1:] هو رقمها، سواء كان من خانة واحدة أو أكثر. ولكي نغيّر العنوان، والجملة لا تتغيّر، نبني جملة جديدة ونضعها في المتغيّر نفسه: addr = "C" + addr[1:].
472 يعني:mark = input("SMD code: ")
digits = mark[:-1]
power = mark[-1]
ohms = int(digits) * 10 ** int(power)
print("Resistance:", ohms, "ohm")SMD code: 472 Resistance: 4700 ohm
وبالبرنامج نفسه من دون أي تغيير، الرمز ذو الأرقام الأربعة 1002 يعطي: Resistance: 10000 ohm
tag = "TMP=31.5"tag[:3]tag[4:]tag[4:6]tag[-3:]tag[::3]tag[::-1]TMP 2 31.5 3 31 4 1.5 5 T=. 6 5.13=PMT sn = "AB-1234-77" print(sn[3:7]) print(sn[-2:]) print(sn[::-1][:2]) print(int(sn[3:5]) + 1)
5.4 التحويل إلى جملة نصية – המרה למחרוזת
الدالة str التي رأيناها في الفصل 2 تأخذ قيمة من أي نوع وتعطي جملة نصية تمثلها. بعد التحويل نستطيع أن نعرف طولها، أو نصل إلى رقم معيّن فيها. والصفر الزائد في 12.50 ليس جزءا من قيمة العدد، ولذلك يختفي:
print(str(12.50)) print(str(True)) print(type(str(1023))) print(len(str(1023))) print(str(1023)[0])
12.5 True <class 'str'> 4 1
والإشارة + لا تصل جملة نصية بعدد ولا تحوّل من تلقاء نفسها، كما رأينا في البند 2.12. لذلك نحوّل بأنفسنا.
battery = 12.42 temp = 31.5 line = "BAT=" + str(battery) + ";TMP=" + str(temp) print(line) print(len(line))
BAT=12.42;TMP=31.5 18
هذا هو السطر الذي سنفكّه في مشروع هذا الفصل. وفي نص نعرضه أو نرسله نقرّب قبل التحويل بالدالة round، وإلا جاءت معه كل الأرقام التي بعد النقطة.
الدالتان bin وhex من الفصل 2 تعطيان جملة نصية، ولذلك نستطيع أن نقصّ البادئة منها. فالتعبير bin(13)[2:] يعطي 1101. والتحويل العكسي، من جملة إلى عدد، هو int وfloat اللذان نعرفهما.
str يبدو طبيعيا، لكنه يغطي الدالة الجاهزة التي تحمل الاسم نفسه، وبعده لا نستطيع أن نحوّل شيئا. لذلك لا نسمي به متغيّرا.n = 407 s = str(n) print(s + s) print(n + n) print(s[1] == 0) print(s[1] == "0")
5.5 المعاملان + و* على الجمل النصية – אופרטורים על מחרוזות
| الأمر | عمله |
|---|---|
| s + t | الوصل: جملة جديدة فيها حروف s ثم حروف t، ولا تُضاف مسافة بينهما |
| s * n | التكرار: جملة جديدة فيها s مكررة n مرة، والعدد n صحيح |
الوصل (שרשור) والتكرار (שכפול) معروفان لنا من الفصل 2. وكما في الحساب، يأتي الضرب قبل الجمع:
name = "BAT"
value = "12.42"
print(name + "=" + value)
print("-" * 20)
print(3 * "01")
print("0" + "1" * 3)
print(len("AB" * 0))BAT=12.42 -------------------- 010101 0111 0
"01" * 2.5 يوقف البرنامج. ولا يوجد طرح بين الجمل النصية: لكي نحذف جزءا من جملة نستعمل القصّ أو replace من البند 5.7.#، والباقي يبقى نقطة.percent = int(input("Battery percent: "))
full = percent // 10
bar = "#" * full + "." * (10 - full)
print("[" + bar + "]", percent)Battery percent: 67 [######....] 67
عندما تكون الشحنة 100 بالمئة يصبح عدد النقاط صفرا، و"." * 0 تعطي جملة فارغة، فلا نحتاج إلى شرط. نفحص: [##########] 100
بناء جملة في حلقة:
الجملة الفارغة هنا هي نقطة البداية، مثل الصفر عند جمع الأعداد: مُجمِّع نصي (צובר) يبدأ فارغا، وفي كل دورة نصل به حرفا. ولأن الجملة لا تتغيّر، فكل دورة تبني جملة جديدة وتضعها في المتغيّر نفسه. نقلب مثلا كل خانة في إطار، كما تفعل البوابة NOT في الأنظمة الرقمية:
bits = "10110001"
result = ""
i = 0
while i < len(bits):
if bits[i] == "1":
result = result + "0"
else:
result = result + "1"
i += 1
print(bits)
print(result)10110001 01001110
والجهة التي نصل منها مهمة: في البرنامج كتبنا الحرف الجديد بعد result، كما في result + "0"، فبقي الترتيب محفوظا. ولو كتبناه قبله، كما في "0" + result، لانقلب الترتيب. وهكذا يعطينا السطر result = bits[i] + result في حلقة ما يعطيه bits[::-1].
"ab" + "c""ab" * 2"1" + "2""=" * 3 + ">"len("ab" * 4)str(1 + 2) + "3"abc 2 abab 3 12 4 ===> 5 8 6 33 s = ""
i = 1
while i <= 3:
s = str(i) + "-" * i + s
i += 1
print(s)
print(len(s))5.6 معامل العلاقة in – אופרטור היחס in
المعامل in يسأل: هل هذه الجملة موجودة داخل تلك، كقطعة متصلة؟ والجواب True أو False. والمعامل not in يعطي العكس. البحث يميّز بين الحرف الكبير والصغير، والجملة الفارغة موجودة في كل جملة:
line = "BAT=12.42;TMP=31.5"
print("=" in line)
print("BAT" in line)
print("bat" in line)
print("HUM" not in line)
print("" in line)True True False True True
in يبحث في كل الجملة، لا في أولها. العنوان LDR1 لحسّاس ضوئي يحتوي على الحرف R، لكن قطعته ليست مقاومة من النوع R. ولنوع القطعة نفحص الحرف الأول. ثم إن طرفي in جملتان نصيتان: 1 in "101" يوقف البرنامج، لأن الطرف الأيسر عدد.addr = "LDR1"
print("R" in addr)
print(addr[0] == "R")True False
واستعمال جميل آخر: عندما نريد أن نعرف هل الحرف واحد من مجموعة، نكتب المجموعة كجملة، وهذا أقصر من سلسلة طويلة من or.
0 و1 فقط. نمر على الحروف، ونتوقف عند أول حرف خاطئ، ونقول أين هو.frame = input("Frame: ")
i = 0
good = True
while i < len(frame) and good:
if frame[i] not in "01":
good = False
else:
i += 1
if good:
print("Valid frame")
else:
print("Bad char", frame[i], "at", i)Frame: 10120010 Bad char 2 at 3
شرط الحلقة مركب من جزأين: الأول يمنع الخروج من الجملة، والثاني يوقف الحلقة عند أول خطأ. لهذا يبقى i على مكان الحرف الخاطئ، ونستطيع أن نطبعه بعد الحلقة.
a = "SW1=ON"
print("ON" in a)
print("on" in a)
print(a[4:] in "ON OFF")
print("1" in a[:3] and "=" in a)5.7 عمليّات على الجمل النصية – פעולות על מחרוזות
الفئة str تحمل عمليّات جاهزة، ونستدعيها بنقطة بعد الجملة، كما تعلمنا في البند 2.3. نمر عليها حسب ترتيبها في المنهاج، وفي آخر الفصل نجدها كلها في جدول واحد.
البحث:
العمليّة find تعطي مؤشّر أول ظهور لما نبحث عنه. هذا ما يجعلها الأداة الطبيعية لتحديد مكان القصّ عندما لا تكون الأماكن ثابتة:
= عند المؤشّر 3: ما قبلها هو الاسم، وما بعدها هو القيمةitem = "TMP=31.5"
print(item.find("="))
print(item.find("#"))
pos = item.find("=")
print(item[:pos])
print(item[pos + 1:])3 -1 TMP 31.5
find القيمة -1، ولا تتوقف ولا تعطي خطأ. لكن -1 مؤشّر صالح عند القصّ: يعني آخر حرف. لذلك يعمل البرنامج التالي بلا شكوى، ويعطي اسما مقطوعا وقيمة فيها الجملة كلها:item = "TMP31.5"
pos = item.find("=")
print(pos)
print(item[:pos])
print(item[pos + 1:])-1 TMP31. TMP31.5
لهذا نفحص النتيجة قبل أن نستعملها، بالمقارنة pos != -1، لا بكتابة if s.find("="): وحدها: القيمة -1 تُحسب صوابا، والقيمة 0، وهي نتيجة صحيحة تعني أن الجملة بدأت بما بحثنا عنه، تُحسب خطأ.
الأحرف الكبيرة والصغيرة:
العمليّتان upper وlower تغيّران الأحرف الأبجدية فقط، ولا تمسّان الأرقام والإشارات. والسطر cmd.lower() وحده لا يفعل شيئا نراه: حُسبت الجملة الجديدة وضاعت، وهذا خطأ شائع. يجب أن نضعها في متغيّر:
cmd = "Led 1 on" print(cmd.upper()) cmd.lower() print(cmd) cmd = cmd.lower() print(cmd)
LED 1 ON Led 1 on led 1 on
الأوامر التي يكتبها المستخدم قد تأتي بأحرف كبيرة أو صغيرة، فنوحّدها عند القراءة: cmd = input("Command: ").upper(). نستطيع أن نكتب العمليّة مباشرة بعد input، لأن ما يعود منه جملة نصية.
العدّ:
العمليّة count تعطي عدد مرات الظهور. وما لم يظهر عدده صفر. البحث يتقدم بعد كل ظهور، ولذلك لا تتداخل الظهورات، كما في السطر الرابع من المُخرَج. والسطر الأخير هو فحص الزوجية (בדיקת זוגיות) الذي نحتاج إليه في الاتصال التسلسلي: هل عدد الخانات التي قيمتها 1 زوجي؟ كان يحتاج إلى حلقة كاملة، وأصبح تعبيرا واحدا.
bits = "10110010"
print(bits.count("1"))
print(bits.count("10"))
print(bits.count("2"))
print("0000".count("00"))
print(bits.count("1") % 2 == 0)4 3 0 2 True
البداية والنهاية:
العمليّتان startswith وendswith تجيبان بـ True أو False. وهما أوضح من القصّ ثم المقارنة، إذ لا نحتاج إلى عدّ الحروف:
addr = "R12"
print(addr.startswith("R"))
print(addr.startswith("r"))
print(addr.endswith("2"))
print("log_05.csv".endswith(".csv"))True False True True
فحص نوع الحروف:
العمليّة isalpha تجيب هل كل حروف الجملة أحرف أبجدية، وisnumeric هل كلها أرقام. والجملة الفارغة تعطي False في الحالتين.
print("LED".isalpha(), "LED1".isalpha())
print("LED 1".isalpha(), "".isalpha())
print("1023".isnumeric(), "12.42".isnumeric())
print("-5".isnumeric(), "".isnumeric())True False False False True False False False
isnumeric يسأل عن الحروف، لا عن المعنى. العدد 12.42 فيه نقطة، والعدد -5 فيه ناقص، والنقطة والناقص ليسا رقمين، ولذلك يكون الجواب False مع أن float يقبل الجملتين. سنحل مسألة النقطة بعد قليل، باستعمال replace.EL-2026-0417 قبل أن نحفظه: حرفان للشركة، أربع خانات للسنة، وأربع لرقم الوحدة. نستعمل elif لنقف عند أول خطأ.sn = input("Serial number: ")
if len(sn) != 12:
print("Wrong length")
elif not sn.startswith("EL-") or sn[7] != "-":
print("Wrong format")
elif not (sn[3:7] + sn[8:]).isnumeric():
print("Bad number")
else:
print("Valid")Serial number: EL-2026-0417 Valid
| المُدخَل | المُخرَج |
|---|---|
EL-2026-417 | Wrong length |
XY-2026-0417 | Wrong format |
EL-20A6-0417 | Bad number |
نفحص الطول أولا لكي يكون sn[7] موجودا بالتأكيد عندما نصل إليه.
الاستبدال:
العمليّة replace تعيد جملة جديدة وضعت فيها الجزء الثاني مكان كل ظهور للجزء الأول. وإذا لم تجده أعادت الجملة كما هي، بلا خطأ. والجزء الثاني قد يكون جملة فارغة، وهذا هو الحذف. وبعض أجهزة القياس تكتب الفاصلة العشرية فاصلة وتضيف الوحدة بعد القيمة، فننظف الجملة قبل float: كل replace تعيد جملة، فنكتب الثانية مباشرة بعد الأولى.
raw = "12,42 V"
print(raw.replace(" V", ""))
print(raw.replace("x", "y"))
print(raw)
clean = raw.replace(",", ".").replace(" V", "")
print(float(clean) * 2)12,42 12,42 V 12,42 V 24.84
نعود إلى مسألة النقطة: نحذف النقطة ونفحص أن ما بقي أرقام، ونفحص أيضا أنه لم تكن هناك أكثر من نقطة واحدة. هذا الفحص قبل float هو قلب مشروع هذا الفصل:
text = input("Value: ")
digits = text.replace(".", "")
if text.count(".") <= 1 and digits.isnumeric():
print("Number:", float(text))
else:
print("Not a number")Value: 12.42 Number: 12.42
| المُدخَل | المُخرَج |
|---|---|
1.2.3 | Not a number |
hot | Not a number |
-5 | Not a number |
التقسيم:
العمليّة split تقطع الجملة عند الفواصل (מפריד) وتعيد قائمة (רשימה) من القطع. بلا وسيط تقطع عند المسافات. وحتى نصل إلى الفصل 6 نحتاج من القائمة إلى ثلاثة أشياء فقط: طباعتها، معرفة عدد عناصرها (איבר) بالدالة len، وقراءة عنصر بالمؤشّر، تماما كما في الجملة النصية.
cmd = "LED 1 ON" parts = cmd.split() print(parts) print(len(parts)) print(parts[0], parts[2]) print(parts[1] == 1) print(int(parts[1]) + 1)
['LED', '1', 'ON'] 3 LED ON False 2
السطران الأخيران مهمان: كل عناصر القائمة جمل نصية، حتى لو بدت أعدادا، فنحوّلها قبل أن نحسب بها. وبالفاصل نقطع أي سطر. وإذا لم يوجد الفاصل حصلنا على قائمة فيها عنصر واحد، هو الجملة كلها.
line = "BAT=12.42;TMP=31.5"
print(line.split(";"))
print(line.split("#"))
print("A B".split())
print("A B".split(" "))
print("A;B;".split(";"))['BAT=12.42', 'TMP=31.5'] ['BAT=12.42;TMP=31.5'] ['A', 'B'] ['A', '', 'B'] ['A', 'B', '']
LED 1 ON. نقسمه إلى كلمات، ونفحص كل جزء قبل أن ننفذ الأمر. ومؤشّر خارج القائمة يوقف البرنامج، ولذلك نفحص len(parts) أولا، قبل أن نقرأ أي عنصر.cmd = input("Command: ").upper()
parts = cmd.split()
if len(parts) != 3:
print("ERR: need 3 words")
elif parts[0] != "LED":
print("ERR: unknown device")
elif not parts[1].isnumeric():
print("ERR: bad number")
elif parts[2] != "ON" and parts[2] != "OFF":
print("ERR: bad state")
else:
print("LED", int(parts[1]), "goes", parts[2])Command: led 1 on LED 1 goes ON
| المُدخَل | المُخرَج |
|---|---|
LED 1 | ERR: need 3 words |
LED one ON | ERR: bad number |
LED 1 DIM | ERR: bad state |
الطول:
الدالة len ترافقنا منذ أول الفصل. نسميها دالة جاهزة لأنها لا تخص الفئة str وحدها: هي تعمل أيضا على القوائم وعلى أنواع أخرى، ولذلك نضع الجملة بين قوسيها. ومن اعتاد على s.upper() يكتبها بعد نقطة، فيأتي الخطأ الذي في آخر المُخرَج:
msg = "ALARM: TEMP HIGH" print(len(msg)) print(len(msg.split())) print(msg.len())
16 3 AttributeError: 'str' object has no attribute 'len'
msg = input("Message: ")
width = 16
if len(msg) > width:
print(msg[:width])
else:
print(msg + "." * (width - len(msg)))Message: ALARM: TEMP TOO HIGH ALARM: TEMP TOO
وللرسالة القصيرة BAT OK نحصل على: BAT OK..........
s = "SW1=OFF;SW2=ON"
print(s.find("="))
print(s.find("X"))
print(s.count("SW"))
print(s.split(";")[1])
print(s.lower().startswith("sw"))
print(s.replace("OFF", "ON").count("ON"))في الفصل 4 كانت المحطة تقرأ أعدادا. أما الأجهزة الحقيقية فترسل نصا، وقد يصل مقطوعا أو فيه تشويش. نكتب البرنامج الذي يتلقى سطرا مثل BAT=12.42;TMP=31.5، يفصل القراءات، يفحص كل قراءة، ولا يقبل السطر إلا إذا كان سليما كله. وهذا هو السطر نفسه الذي سيرسله أردوينو في الفصل 10.
split يفصل القراءات، find يحدد مكان القصّ في كل قراءة، وfloat يحوّل القيمةline = input("Line: ")
parts = line.split(";")
battery = -1.0
temp = -1.0
errors = 0
i = 0
while i < len(parts):
item = parts[i]
pos = item.find("=")
if pos == -1:
print("No '=' in:", item)
errors += 1
else:
name = item[:pos]
text = item[pos + 1:]
digits = text.replace(".", "")
if text.count(".") > 1 or not digits.isnumeric():
print("Bad value in:", item)
errors += 1
elif name == "BAT":
battery = float(text)
elif name == "TMP":
temp = float(text)
i += 1
if errors == 0 and battery >= 0 and temp >= 0:
print("Battery:", battery, "V")
print("Temp:", temp, "C")
else:
print("Line rejected")Line: BAT=12.42;TMP=31.5 Battery: 12.42 V Temp: 31.5 C
نفحص القراءة بخطوتين: هل فيها =؟ وهل القيمة عدد؟ والقيمة -1.0 في البداية تعني أننا لم نقرأ بعد؛ فإذا بقيت كذلك بعد الحلقة، فقد نقصت قراءة. وهذه نتائج البرنامج على سطور فيها عيوب:
| السطر | المُخرَج |
|---|---|
BAT=12.42;TMP=hot | Bad value in: TMP=hot / Line rejected |
BAT12.42;TMP=31.5 | No '=' in: BAT12.42 / Line rejected |
BAT=12.42 | Line rejected |
BAT=12.42;TMP=31.5; | No '=' in: / Line rejected |
نجرّب بأنفسنا: البرنامج يتجاهل قراءة اسمها غير معروف، مثل HUM=40. ماذا نضيف لكي يرفض السطر عندها؟
أهمّ أفكار الفصل:
- الجملة النصية سلسلة حروف مرتبة، ومؤشّر أولها
0ومؤشّر آخرها-1. - الجملة لا تتغيّر: كل عمليّة تعيد قيمة جديدة، وعلينا أن نحفظها.
- القصّ
s[a:b]لا يشملb، ولا يعطي خطأ عند تجاوز الحدود، أما المؤشّر فيعطي خطأ. - الجملة الفارغة هي نقطة البداية لبناء جملة في حلقة.
findيعطي-1عندما لا يجد، و-1مؤشّر صالح، فنفحص قبل أن نقصّ.splitتعيد قائمة من جمل نصية، ونحوّل عناصرها قبل الحساب.- لا نثق بنص وصل من الخارج: نفحصه أولا، ثم نحوّله.
ملخّص أوامر الفصل:
| الأمر | عمله |
|---|---|
| "" len(s) | الجملة الفارغة، وعدد الحروف: دالة جاهزة وليست عمليّة للكائن |
| s[i] s[-1] | حرف بالمؤشّر، والحرف الأخير |
| s[a:b] s[a:] s[:b] | قطعة من الجملة، من دون الحرف الذي مؤشّره b |
| s[a:b:c] s[::-1] | قطعة بخطوة، والجملة مقلوبة |
| str(x) | التحويل إلى جملة نصية |
| s + t s * n | الوصل والتكرار |
| x in s x not in s | هل x قطعة من s |
| s.find(x) | مؤشّر أول ظهور، أو −1 إذا لم يوجد |
| s.upper() s.lower() | جملة جديدة بأحرف كبيرة، أو بأحرف صغيرة |
| s.count(x) | عدد مرات الظهور |
| s.startswith(x) s.endswith(x) | فحص البداية والنهاية |
| s.isalpha() s.isnumeric() | فحص نوع الحروف |
| s.replace(a, b) | الاستبدال والحذف |
| s.split() s.split(x) | قائمة القطع بين المسافات، أو بين الفواصل x |
قاموس المصطلحات – מילון מונחים:
| بالعربية | بالعبرية (كما تظهر في الامتحان) | English |
|---|---|---|
| الجملة النصية | מחרוזת | String |
| الجملة النصية الفارغة | מחרוזת ריקה | Empty string |
| الحرف | תו | Character |
| المؤشّر | אינדקס | Index |
| القصّ | חיתוך | Slicing |
| الخطوة | צעד | Step |
| الوصل | שרשור | Concatenation |
| التكرار | שכפול | Repetition |
| غير قابلة للتغيير | בלתי ניתנת לשינוי | Immutable |
| معامل العلاقة | אופרטור יחס | Relational operator |
| الفاصل | מפריד | Separator |
| قائمة | רשימה | List |
| عنصر | איבר | Element |
4 , False , False , True215 , A1234 , 77 , 77 , 13407407 , 814 , False , True3---2--1- , 9True , False , True , True3 , -1 , 2 , SW2=ON , True , 2أسئلة للتمرين:
tag = "SW-07"
print(tag[0])
print(tag[-1])
print(tag[3:])
print(len(tag))
print(tag.find("-"))
print(tag[::-1])S , 7 , 07 , 5 , 2 , 70-WS(أ)
"A5".isalpha()(ب)
"105".isnumeric()(ج)
"1.5".isnumeric()(د)
"LED".lower()(هـ)
"ON OFF".find("F")(و)
"ON OFF".count("O")False ، True ، False ، led ، 4 ، 2A5 أو D13، ويطبع الحرف والرقم منفصلين.port = input("Port: ")
print("Letter:", port[0])
print("Number:", int(port[1:]))D13 يطبع: Letter: D / Number: 13X. نجد الخطأين ونصلحهما.cmd = "led on" cmd.upper() cmd[0] = "X" print(cmd)
cmd = "led on" cmd = cmd.upper() cmd = "X" + cmd[1:] print(cmd)
XED ON104 يعني 10 مضروبا في 104 بوحدة البيكوفاراد. نكتب برنامجا يقرأ رمزا من ثلاثة أرقام، ويطبع السعة بالبيكوفاراد وبالنانوفاراد.mark = input("Capacitor code: ")
pf = int(mark[:2]) * 10 ** int(mark[2])
print(pf, "pF")
print(pf / 1000, "nF")104 يطبع: 100000 pF / 100.0 nFin وreplace، ثم يطبع عدد الكلمات.msg = input("Message: ")
while " " in msg:
msg = msg.replace(" ", " ")
print(msg)
print(len(msg.split(" ")))TEMP OK NOW يطبع: TEMP OK NOW / 3. الحلقة ضرورية: استبدال واحد لا يكفي عندما تتتالى ثلاث مسافات أو أكثر.0 و1 فقط، يطبع رسالة خطأ. وإلا يطبع الإطار وبعده خانة تجعل عدد الخانات 1 زوجيا. نفعل ذلك من دون حلقة.frame = input("Frame: ")
ones = frame.count("1")
zeros = frame.count("0")
if ones + zeros != len(frame) or frame == "":
print("Bad frame")
else:
print(frame + str(ones % 2))1011 يطبع: 10111 ، وللمُدخَل 10a1 يطبع: Bad frameR1,R12,C3,R7,L2. نكتب برنامجا يطبع عدد القطع كلها، وعدد المقاومات منها.board = input("Parts: ")
parts = board.split(",")
count = 0
i = 0
while i < len(parts):
if parts[i].startswith("R"):
count += 1
i += 1
print(len(parts), count)5 3int(text, 16): نحفظ الخانات بالترتيب في جملة، وfind يعطينا قيمة كل خانة. ثم نفحص النتيجة بواسطة int(text, 16).text = input("Hex: ").upper()
table = "0123456789ABCDEF"
value = 0
i = 0
while i < len(text):
value = value * 16 + table.find(text[i])
i += 1
print(value)
print(int(text, 16))1f4 يطبع: 500 / 500* وبعدها طول الرسالة، مثل HELLO*5. نكتب برنامجا يفحص ذلك.frame = input("Frame: ")
pos = frame.find("*")
if pos == -1:
print("No length field")
else:
body = frame[:pos]
size = frame[pos + 1:]
if size.isnumeric() and int(size) == len(body):
print("Good:", body)
else:
print("Length mismatch")HELLO*5 وHELL*5 وHELLO يطبع: Good: HELLO / Length mismatch / No length fieldA0=2.5;A1=4.75;A2=1.2، وكلها سليمة. نكتب برنامجا يطبع اسم أعلى قراءة وقيمتها.line = input("Line: ")
parts = line.split(";")
top = 0.0
top_name = ""
i = 0
while i < len(parts):
pos = parts[i].find("=")
value = float(parts[i][pos + 1:])
if i == 0 or value > top:
top = value
top_name = parts[i][:pos]
i += 1
print(top_name, top)A1 4.75text = input("Value: ")
body = text
if text.startswith("-"):
body = text[1:]
digits = body.replace(".", "")
if body.count(".") <= 1 and digits.isnumeric():
print("Number:", float(text))
else:
print("Not a number")-3.5 و12 و3-5 يطبع: Number: -3.5 / Number: 12.0 / Not a number