טיפוס מחרוזת: str#

אג’נדה#

  • הסבר על הטיפוס str

  • פעולות בסיסיות על מחרוזות (+, *, in, len)

  • גישה לחלקים מהמחרוזת באמצעות אינדקסים

  • פעולות מתקדמות על מחרוזות (מתודות)

  • מחרוזת - אובייקט בלתי ניתן לשינוי (immutable)

  • ייצוג מחרוזת במחשב

  • פעולות השוואה

בחלק זה נלמד איך לייצג טקסט בפייתון. לשם כך נשתמש בערכים מסוג מחרוזת (string). בפייתון, שם הטיפוס הוא str.

לנוחיותכם חלונית עם הקוד שראינו בסרטון. מוזמנים להריץ בעצמכם.

# print(Hello world!)
# print(3)
# print("3")
# print(3 + "3")
s = "This is a string"
# print(s)
print("3+4 =", 3*4)
3+4 = 12

בחנו את בעצמכם

בתרגיל זה תדפיסו מחרוזת מורכבת שתדפיס את שמכם ואת גילכם.

תחילה, הגדירו שני משתנים:

  • המשתנה הראשון ייקרא name וערכו יהיה מחרוזת עם השם שלכם.

  • המשתנה השני ייקרא age וערכו יהיה הגיל שלכם בשנים.

לדוגמה: סטודנטית בת 23 בשם עדי תגדיר את המשתנים הבאים:

name =  "Adi"
age =  23

לאחר מכן, עליכם להדפיס מחרוזת מורכבת (תוך שימוש בפקודה print עם פסיקים, כפי שהודגם בסרטון) שתדפיס את השם ואת הגיל לפי הפורמט הבא:

"My name is <your name> and I am <your age> years old"

כאשר במקום יופיע השם שלכם כפי שהוגדר במשתנה name, ובמקום יופיע הגיל שלכם כפי שהוגדר במשתנה age. למשל, כאשר הסטודנטית עדי, אותה פגשנו זה עתה, תריץ את הקוד שלה, יודפס:

"My name is Adi and I am 23 years old"

רמז: כדי לייצר מחרוזות מורכבות יש להפריד בין חלקי המחרוזת בעזרת פסיקים.

### write your code here

אופרטורים על מחרוזות#

לנוחיותכם חלונית עם הקוד שראינו בסרטון. מוזמנים להריץ בעצמכם.

# print("Hello " + "World")
# print("Hello" + " " + "World")
# print("Hello" * 3)
s1 = "Hello"
s2 = s1 * 3
# print(s2)
# print(("Hello" + "World") * 2)
# print(len("a b c d e"))
# print(len(""))
# print("H" in "Hello")
# print("ell" in "Hello")
# print("G" in "Hello")
print("bell" in "Hello")
False

בחנו את בעצמכם

מה מודפס בקטע הקוד הבא:

s="ab"
print(s*(len(s)+len("")))

פעולות נוספות של מחרוזות#

לנוחותכם חלונית עם הקוד שראינו בסרטון. מוזמנים להריץ בעצמכם:

#print("ab" == "ab")
#print("ab" != "ab")
#print("ab" < "cd")
#print("abc" < "!@#")
s = "Python"
# print(s[0])
# print(s[1])
# print(s[2])
# print(s[3])
# print(s[4])
# print(s[5])
#print(s[6])
print(s[len(s) - 1])
n

סיכום: אופרטורים של מחרוזות#

נניח כי ביצענו את ההשמות הבאות:

a = 'Hello'
b = 'Python'

אופרטור

תיאור

דוגמה

+

שרשור - מחבר ערכים משני צידי האופרטור

a + b יתן 'HelloPython'

*

חזרה - יוצר מחרוזות חדשות ע״י שרשור חוזר של אותה מחרוזת

a*2 יתן 'HelloHello'

in

בדיקת חברות - מחזיר אמת אם תת מחרוזת קיימת במחרוזת

'H' in a יתן True

not in

בדיקת אי-חברות - מחזיר אמת אם תת מחרוזת לא קיימת במחרוזת

'M' not in a יתן True

len

בדיקת מספר התווים במחרוזת/אורך המחרוזת

len("test") יתן 4

גישה לתווים במחרוזות#

לאחר שקיבלנו את האינדקס, או אם אנחנו יודעים אותו מראש - ראינו בסרטון שניתן לגשת אל התו שנמצא באותו מקום במחרוזת באמצעות סוגריים מרובעים: []. תזכורת חשובה - אינדקסים בפייתון מתחילים ב־0, כלומר התו הראשון במחרוזת נמצא באינדקס 0, השני באינדקס 1, וכן הלאה.
ניתן גם לספור תווים מהסוף - התו האחרון במחרוזת נמצא גם באינדקס 1-, התו לפני האחרון נמצא באינדקס 2- וכן הלאה.

דוגמה: המחרוזת “Hello” והאינדקסים התואמים כל אות.

a='Hello'
print(a[0])
print(a[1])
print(a[-1])
print(a[4])
H
e
o
o

גם באינדקסים שליליים - ומה יקרה אם ננסה לגשת לאינדקס שלא קיים במחרוזת?

print(a[-6])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[10], line 1
----> 1 print(a[-6])

IndexError: string index out of range

Slicing של מחרוזת#

בפייתון ניתן לגשת לא רק לתו אחד במחרוזת, אלא גם לחתיכה (slice) שלמה ממנה.
התחביר הוא: string[start:end:step]

  • האינדקס הראשון (start) כלול בחיתוך.

  • האינדקס האחרון (end) לא כלול - החיתוך נעצר לפניו.

  • גודל הצעד הבא (step). בברירת המחדל הגודל הוא 1. לדוגמא:

text="hello world"
print(text[0:7])
hello w

בדוגמא לעיל הדפסנו את שבעת התווים הראשונים - מ-0 (כולל) עד 7 (לא כולל).

שימו לב שרווח נחשב גם הוא כתו

דוגמא נוספת:

print(text[0:7:3])
hlw

גם בדוגמא זו הדפסנו תווים משבעת האינדקסים הראשונים, אך בפעם ביצענו קפיצות של 3 בין כל זוג תווים (במקום קפיצה של 1). כלומר, הדפסנו את התווים המופיעים באינדקסים 0, 3 ו-6

בואו נתרגל זאת על המחרוזת האהובה עלינו:

a = 'Hello'
print(a[1])
print(a[1:3])
e
el

ערך step שלילי#

אם הערך של step הוא שלילי, אזי הכיוון שבו נעבור לאינדקס הבא יהיה מהסוף להתחלה, כלומר נרוץ “אחורה” על המחרוזת.
במקרה זה אם האינדקס start יהיה קטן או שווה מהאינדקס end תתקבל מחרוזת ריקה.

בדוגמא להלן, מתחילים לרוץ מהאינדקס 4 לאחור עד לאינדקס 1:

a[4:1:-1]
'oll'

ערכי ברירת מחדל לאינדקסים start וend#

אם נשאיר את start ריק, אזי start ייבחר אוטומטית להיות 0 - תחילת המחרוזת.

אם נשאיר את end ריק, אזי end ייבחר אוטומטית להיות len(string) - כך שהחיתוך יגיע לסוף המחרוזת.

print(a[:5])
print(a[1:])
Hello
ello

לעומת זאת, אם הערך של step הוא שלילי, אזי ערכים ברירת המחדל של האינדקסים start ו-end יהיו הפוכים: start יהיה סוף המחרוזת, והאינדקס end יהיה תחילת המחרוזת

a[::-1]
'olleH'

ערכים שליליים לאינקסים#

כפי שלמדנו, ניתן להשתמש גם באינדקסים השליליים לציון התווים במחרוזת.

print(a[-4:-2])
print(a[:-3])
print(a[-3:])
el
He
llo

תרגול: פלינדרום#

את השפה העברית אנחנו כותבים וקוראים בד”כ מימין לשמאל. כך גם בערבית. לעומת זאת בשפות כמו אנגלית, רוסית, צרפתית וגם באמהרית כותבים וקוראים משמאל לימין. בכל השפות האלו ובאחרות ישנן מילים שאפשר לקרוא אותן בשני הכיוונים באותו אופן. למשל, המילה ‘אמא’ בעברית, או המילה radar באנגלית. מילים כאלו נקראות “פלינדרום”.

כעת ננסח בצורה מעט יותר פורמלית את בעית הפלינדרום. הקלט לבעיה שלנו הוא מחרוזת. אם המחרוזת ניתנת לקריאה בשני הכיוונים באופן זהה הפלט יהיה True.

דוגמא למחרוזת שהן פלינדרום:

  • 21.11.12

  • alula

  • anna

  • deified

כעת השלימו את הפונקציה הבאה, המחזירה True אם המחרוזת text בקלט היא פלינדרום, וFalse אחרת.

def is_palindrome(text):
    # Your code here

print(is_palindrome('abba'))
print(is_palindrome('abbc'))
  Cell In[20], line 4
    print(is_palindrome('abba'))
    ^
IndentationError: expected an indented block after function definition on line 1

כדאי לדעת!

זיהוי פלינדרום היא בעיה קלאסית במדעי המחשב. בהמשך הקורס, נפגוש בה שוב, ונעמיק בגישות שונות לפתרונה.

פונקציות ומתודות של מחרוזות#

כפי שראינו, הטיפוס str בפייתון מייצג טקסט.
ישנן מגוון פעולות שונות שנרצה לבצע על טקסט, מימוש של כל פעולה כזו נקרא פונקציה.

כבר נתקלתם בפונקציה אחת: len - המחזירה את אורך של אובייקט. במקרה שלנו, הפעלנו את הפונקציה על אובייקט מסוג מחרוזת, וכך קיבלתנו את מספר התווים במחרוזת.

בניגוד לפונקציה len, ישנן פונקציות נוספות שהן ייחודיות - או משויכות - לטיפוס מסוים פונקציות אלו נקראות גם מתודות.

למתודות של תחביר הפעלה מעט שונה משל פונקציות רגילות. כאן אנו כותבים קודם את שם המשתנה (או באובייקט) עליו רוצים לבצע את הפעולה, ולאחר מכן את שם פעולה (או המתודה), כאשר מפרידים ביניהם באמצעות נקודה:

variable_name.method_name(...)

לדוגמא, נניח נרצה שנרצה לחפש היכן מופיע תו כלשהו במחרוזת. לצורך כך נשתמש בפעולה find, שמחזירה את האינדקס (=המיקום) של ההופעה הראשונה של התו/המחרוזת שחיפשנו. אם החיפוש לא מצליח – הפונקציה מחזירה -1.
על מנת להפעיל את המתודה find על המשתנה a כדי לחפש את המופע הראשון של H, נכתוב את השורה a.find('H'):

a = 'Hello'
print(a.find('H'))

בדומה לfind, קיימות מתודות נוספות.#

ערכו את הקוד להלן, ונסו להבין בעצמכם מה עושות המתודות הבאות:

txt="my name is Assaf"
print(txt.upper())
print(txt.lower())
print(txt.replace("sa", "i"))
print(txt.title())
MY NAME IS ASSAF
my name is assaf
my name is Asif
My Name Is Assaf

כפי שניתן לראות, קיימות פעולות רבות ושימושיות לעבודה עם מחרוזות.
אנחנו ממליצים לכם כבר עכשיו להתחיל להכין מסמך אישי שבו תרכזו את כל הפעולות שלמדתם. המסמך הזה ילווה אתכם לאורך הקורס, יעזור בשיעורי הבית, בהכנה למבחן, ואפילו יהפוך לכלי שימושי בהמשך דרככם כמתכנתים.

להלן רשימת פעולות שימושיות במיוחד שכדאי להכניס למסמך. חפשו עליהן מידע בגוגל או בעזרת מודל AI כדי להבין מה הן עושות ואיך משתמשים בהן:

  • len

  • find, startswith, endswith

  • isalpha, isdigit, islower

  • join, replace

  • strip, rstrip

  • split

חשוב מאוד להכיר את פעולות אלו – הן יאפשרו לכם לפתור בעיות רבות!

שימו לב שמתודות של מחרוזות אינן משנות את המחרוזת המקורית ממנה הן הופעלו

  • למעשה, לא ניתן לשנות את המחרוזת המקורית מכיוון שהיא אובייקט שאינה ניתנת לשינוי. נרחיב על עקרון זה בהמשך המחברת.

:חשבו

איך בכל זאת אפשר לשנות את ערכו של משתנה מטיפוס מחרוזת ממנו הופעלה מתודה מסוימת?

למידע נוסף: Python Strings – TutorialsPoint מומלץ גם לחפש בגוגל שיטות נוספות של str כדי להרחיב את ארגז הכלים שלכם.

מחרוזות הן אובייקטים בלתי ניתנים לשינוי (immutable objects)#

למדנו שניתן להסתכל על תו מסוים במחרוזת על ידי גישה לאינדקס - a[0].

למדנו גם שניתן לבצע השמה - לקחת ערך מסוים, ולהכניס אותו למשתנה.

אז מה יקרה אם נרצה לערוך מחרוזת? לדוגמה - להחליף את התו הראשון במחרוזת באות אחרת?

a = "Hello"
a[0] = "J"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[39], line 2
      1 a = "Hello"
----> 2 a[0] = "J"

TypeError: 'str' object does not support item assignment

מחרוזות הן טיפוס immutable - כלומר לא ניתנים לשינוי אחרי יצירתם. זה אומר שלא ניתן לערוך מחרוזת קיימת.

הדרך היחידה “לשנות” מחרוזת בתוך משתנה קיים, היא ליצור מחרוזת חדשה עם השינוי שרצינו ולבצע השמה מחדש (=).

s = "J" + a[1:]
print(s)

לדוגמא לעיל ביצענו למעשה מספר פעולות בשורה אחת:

  1. a[1:] - יצירת מחרוזת חדשה המכיל את כל התווים של המחרוזת במשתנה a מלבד התו הראשון

    • שימו לב כי המחרוזת בa איננה משתנה!

  2. חיבור האות “J” עם המחרוזת שנוצרה ב(1)

  3. השמה של תוצאת החיבור מ(2) בתוך s

ניתן לשמור את המחרוזת החדשה במשתנה בעל אותו שם, ובכך לדרוס את הערך הקודם, וכך המחרוזת חדשה תשמר במשתנה בעל השם הרצוי.

a="Hello"
print(a)
a="J" + a[1:] 
print(a)
Hello
Jello

כפי שציינו קודם, גם מתודות לא משנות את האובייקט מקורי מהן הופעלו.
לדוגמא:

לדוגמא, הפעולה a.upper() מחזירה את המחרוזת באותיות גדולות:

a="Hello"
print(a.upper())
HELLO

אך המתודה איננה מעדכנת את ערך במשתנה a

a.upper()
print(a)
Hello

לכן, בדומה למקרה הקודם, אם ברצוננו לעדכן את a, נשתמש בהשמה על מנת לעדכן את a בערך החדש - a.upper()

a=a.upper()
print(a)
HELLO

מסקנה: כל פעולה על מחרוזות מחזירה משתנה חדש במקום לשנות את המקורי.

a = "hello"
b = a.title()
print("a value is:", a)
print("b value is:", b)

ייצוג מחרוזות במחשב#

תווי בקרה#

בפייתון קיימים תווי בקרה (escape characters) שנכתבים עם \ ומשמשים לייצוג תווים מיוחדים. למשל, שני תווים נפוצים הם:

  • n\ מייצג ירידת שורה

  • t\ מייצג טאב

טבלת ASCII#

עד עכשיו למדנו איך לעבוד עם מחרוזות בתוך פייתון, אבל כדי להבין מה באמת קורה “מאחורי הקלעים” חשוב לדעת גם איך המחשב שומר וקורא מחרוזות בזיכרון. לכל תו במחרוזת יש מספר - לפי טבלת ASCII (ראשי תיבות של American Standard Code for Information Interchange).

טבלת ASCII מקשרת (או מקודדת) בין תווים למספרים, כך שמחשב יוכל להבין טקסט. לכל תו — כמו אות, ספרה או סימן — יש ערך מספרי קבוע (למשל ‘A’ = 65, ‘0’ = 48). כך מחשבים שונים יכולים לייצג ולהעביר טקסט בצורה אחידה.

להלן טבלת הASCII - בה מופיעים כל התווים (עמודת char) והייצוג המספרי שלהם (עמודת Dec).

כדי להמיר מתו למספר ולהיפך, ניתן להשתמש בפונקציות chr וord:

print(chr(77))
print(ord("M"))
M
77

אופרטורי השוואה בין מחרוזות#

אחרי שלמדנו קידוד תווים באמצעות טבלת ASCII נרחיב על השוואה בין מחרוזות.

כפי שלמדנו מוקדם יותר ביחידה זו, ניתן להשתמש באופרטורי ההשוואה גם עבור מחרוזות. כאשר משווים בין מחרוזות בפייתון, ההשוואה מתבצעת בצורה לקסיקוגרפית לפי ערכי ה־ASCII של התווים. המשמעות היא שהמחשב בודק כל תו במחרוזת לפי סדר הופעתו, ומשווה את הערך המספרי שלו בטבלת ASCII לתו המקביל במחרוזת השנייה. אם התווים הראשונים שווים, ההשוואה ממשיכה לתווים הבאים, עד שנמצא הבדל או עד שאחת המחרוזות מסתיימת.

שימו לב שהתווים האלפבתים באים ברצף: אותיות גדולות נמצאות בטווח 65-90 ואותיות קטנות נמצאות בטווח 97-122, כך שיחס הסדר בין מחרוזות שבכולן אותיות גדולות בלבד או קטנות בלבד דומה ליחס הסדר במילון אנגלי. למשל, “apple” < “banana” יחזיר True כי האות ‘a’ מופיעה לפני ‘b’ בטבלת ASCII.

לעומת זאת, “Zoo” < “apple” יחזיר גם הוא True, משום שהאות ‘Z’ גדולה באותיות גדולות אבל הערך שלה ב־ASCII נמוך יותר מהערך של ‘a’.

לכן חשוב לשים לב שבעת השוואת מחרוזות, האותיות קטנות וגדולות נחשבות שונות, והסדר מושפע ישירות מהקוד המספרי של כל תו ולא מההיגיון הלשוני שלנו.

דוגמא נוספת: “[” > “HELLO” מכיוון בערך הASCII של התו “[” גדול מזה של התו H

print('apple'<'banana')
print('Zoo'<'apple')
print('HELLO'>'[')
print('python'=='Python') # Why?
True
True
False
False

בחנו את עצמכם:

באופן דומה לחיפוש במילון: אם התו הראשון זהה, אז נבצע את ההשוואה לפי התו השני.

לבסוף, אם הגענו לסוף המחרוזת בלי שמצאנו תו שונה, אז המחרוזת הקצרה יותר היא הקטנה.