פעולות על מחרוזות
כל מה שמגיע לתוכנית מן העולם שבחוץ מגיע כטקסט: מה שהמשתמש מקליד, מה שהארדואינו שולח דרך היציאה, ומה שנקרא מקובץ. גם הקריאה 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 שואל: האם המחרוזת הזאת נמצאת בתוך ההיא, כקטע רצוף? התשובה היא 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