קבצים

הגדרה

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

סוג הקובץ תכלית הקובץ דוגמאות לסיומות
טקסט מכיל מלל בלבד, ללא עיצוב כלשהו (הדגשות, גדלים וכדומה) txt
תמונה ייצוג של תמונה דיגיטלית למטרת הצגה חזותית שלה png, jpg, gif, bmp
וידיאו ייצוג של סרט או כל וידיאו אחר mp4, avi, flv
פייתון מכיל קוד שהתוכנה של פייתון יודעת לקרוא ולהפעיל py, pyc, pyd
הרצה מכיל סדרת הוראות המיועדות לקריאה ולהרצה על־ידי המחשב exe, dmg

הקדמה

מהברזל ועד הקובץ

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

הכונן הקשיח

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

ביט

כוננים קשיחים מכילים הרבה יחידות קטנטנות שכל אחת מהן נקראת "ביט".
ביט הוא ערך שיכול להיות 0 או 1, כלומר כבוי או דולק.
בכל כונן קשיח פשוט שנמכר כיום יש מקום למאות מיליארדי(!) ביטים כאלו.
כך נשמר כל המידע שדיברנו עליו בפסקה הקודמת.

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

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

ייצוג תווים

בעיה דומה של ייצוג טקסט באמצעות אפסים ואחדים בלבד, עלתה אי שם ב־1960.
אחסוך לכם הרבה מעשיות בדרך, אבל מפה לשם הוחלט על תקן בשם ASCII, שקובע שכל סידור אפשרי של 8 ביטים שכאלו, שנקרא "בייט", ייצג תו.
האות A קיבלה את הייצוג 01000001, האות Z, למשל, קיבלה את הייצוג 01011010, הספרה 7 את הייצוג 00110111 והתו רווח את הייצוג 00100000.
כך אפשר לקרוא מסר ארוך מאוד, ולהמיר כל 8 ביטים רצופים לתו. אם ננסה לקרוא בשיטה הזו 80 ביטים, נקבל 10 תווים קריאים.

ייצוג קבצים אחרים

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

כשאנחנו רוצים לפתוח תמונה של חתול, למשל, אנחנו מפעילים תוכנה ייעודית שיודעת לקרוא ייצוג של תמונות.
מה שקורה באותו רגע מאחורי הקלעים הוא שהתוכנה קוראת רצפים של 0 ו־1 מהדיסק הקשיח, אותם ביטים שדיברנו עליהם.
מי שצילם את התמונה השתמש בתוכנה שיודעת להמיר את מה שקרה על מסך המצלמה לביטים שנשמרו על כרטיס הזיכרון שלו.
מי שתכנת את התוכנה שמציגה לנו כרגע את החתול ידע להורות לה מה לעשות כדי לתרגם את אותם ביטים לתמונה שמוצגת לכם על המסך.
שניהם פעלו לפי תקן מסוים (יש כמה כאלו, אולי אתם מכירים חלק מהם: JPG, PNG, GIF ועוד), שקובע דרך אחידה לייצג תמונה בעזרת ביטים.
בסופו של דבר, עבור התוכנה החתול שלכם הוא בסך הכול משבצות קטנטנות בצבעים שונים שמצוירות זו על יד זו.

ייצוג מסוים של קובץ נקרא "פורמט", או בעברית "תַּסְדִיר".

קבצים טקסטואליים וקבצים בינאריים

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

מימין ניתן לראות קובץ CSV פתוח באקסל, ומשמאל את הייצוג הטקסטואלי שלו.

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

לפניכם דוגמה לכמה סוגי קבצים בינאריים:

  • MP3 – תסדיר המיועד עבור קובצי שמע.
  • PNG – תסדיר לייצוג תמונות.
  • PDF – תסדיר המיועד עבור תצוגה מדויקת של מסמכים.
  • XLSX – תסדיר המיועד לאחסון מידע בגיליונות אלקטרוניים (לדוגמה, בעזרת Excel).
  • EXE – תסדיר המיועד למערכת ההפעלה חלונות, ומפרט אילו פקודות יש לבצע כדי שתוכנה תרוץ.
מימין ניתן לראות קובץ PNG פתוח בתוכנה להצגת תמונות, ומשמאל את הייצוג הבינארי שלו כשמנסים להמיר אותו לטקסט.
קל לראות ש־PNG אינו תסדיר טקסטואלי.

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

טיפול בקבצים

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

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

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

פתיחת קובץ

הפונקציה open מאפשרת לנו לפתוח קובץ בעזרת פייתון, כדי להשתמש בו בהמשך התוכנית.
היא מקבלת 2 פרמטרים: הראשון הוא הנתיב לקובץ, והשני הוא צורת הגישה לקובץ, שעליה מייד נסביר.
הפונקציה מחזירה ערך שנקרא File handler, מעין מצביע על הקובץ שעליו נוכל לבצע פעולות.

צורת הגישה לקובץ תיבחר לפי המטרה שלשמה אנחנו פותחים אותו:

  • אם אנחנו מעוניינים לקרוא את הקובץ, צורת הגישה שנבחר תהיה 'r' – קריאה, read.
  • אם אנחנו מעוניינים לכתוב אל הקובץ ולדרוס את מה שקיים בו, צורת הגישה שנבחר תהיה 'w' – כתיבה, write.
  • אם אנחנו מעוניינים להוסיף אל הקובץ, צורת הגישה שנבחר תהיה 'a' – הוספה, append.

הפרמטר השני, צורת הגישה לקובץ, הוא מחרוזת.
טעות נפוצה היא לשים שם r, w או a בלי גרשיים סביב.

נתחיל בפתיחת הקובץ, ובהשמה של ה־file handler למשתנה:


In [ ]:
common_passwords_file = open('resources/passwords.txt', 'r')

כך נראה הסוג של file handler המצביע לקובץ טקסטואלי:


In [ ]:
type(common_passwords_file)

קריאת קובץ

עכשיו, כשהקובץ פתוח, נוכל להשתמש בו.
נבקש מפייתון לקרוא את תוכן הקובץ באמצעות הפעולה read, ונבצע השמה של התוכן שחזר מהפעולה, למשתנה:


In [ ]:
common_passwords = common_passwords_file.read()
print(common_passwords)

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


In [ ]:
type(common_passwords)

זיכרו ששורות חדשות מיוצגות על ידי התו \n, וכך גם ב־common_passwords:


In [ ]:
common_passwords

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

הסמן

אם ננסה לקרוא שוב את הקובץ, נגלה תופעה מוזרה מעט:


In [ ]:
common_passwords_again = common_passwords_file.read()
print(common_passwords_again)

In [ ]:
# ???
common_passwords_again == ''

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

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

כאשר אנחנו פותחים קובץ לקריאה בעזרת פייתון, הסמן (באנגלית: cursor) מצביע לתחילת הקובץ.
ברגע שאתם מבקשים מפייתון לקרוא את הקובץ בעזרת הפעולה read, היא קוראת מהמקום שבו נמצא הסמן ועד סוף הקובץ.
בזמן הקריאה הסמן יעבור לסוף הקובץ, ולכן כשתנסו לקרוא אותו שוב – תקבלו מחרוזת ריקה.

seek

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


In [ ]:
common_passwords_file.seek(0)

In [ ]:
print(common_passwords_file.read())

tell

ניתן לראות איפה הסמן נמצא באמצעות הפעולה tell:


In [ ]:
common_passwords_file.tell()

In [13]:
common_passwords_file.read()


Out[13]:
''

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

קריאה בצורות נוספות

לעיתים, בייחוד כאשר מדובר בקובץ גדול, נעדיף לקרוא בכל פעם רק חלק מהקובץ.
נוכל לבחור לקרוא מספר מסוים של תווים באמצעות הפעולה read שאנחנו כבר מכירים,
אלא שהפעם נעביר לה ארגומנט שיורה לה כמה תווים לקרוא:


In [ ]:
common_passwords_file.seek(0)
common_passwords_file.read(10)

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


In [ ]:
common_passwords_file.read(5)

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


In [ ]:
common_passwords_file.seek(0)
common_passwords_file.readline()

לקריאת כל השורות בקובץ, ניתן להשתמש בפעולה readlines, שתחזיר לנו רשימת מחרוזות.
כל מחרוזת ברשימה מייצגת שורה אחת בקובץ:


In [ ]:
common_passwords_file.seek(0)
passwords = common_passwords_file.readlines()
print("The passwords variable looks like: " + str(passwords))
print("The type of 'passwords' is: " + str(type(passwords)))
# אם נרצה להיפטר מתו השורה החדשה באחת השורות, נוכל להשתמש בפעולה שלמדנו על מחרוזות:
# strip
print("The most common password in the list is: " + passwords[0].strip())
print("The least common password in the list is: " + passwords[-1].strip())

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

סגירת קובץ

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

לא מדובר בפעולה מסובכת מדי. כל מה שתצטרכו לעשות הוא להשתמש בפעולה close:


In [ ]:
common_passwords_file.close()

שימו לב שכל ניסיון לעשות שימוש בקובץ אחרי סגירתו, ייכשל:


In [ ]:
common_passwords_file.read()

כתיבה לקובץ

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


In [ ]:
pokemons = """
#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp. Atk,Sp. Def,Speed,Generation,Legendary
1,Bulbasaur,Grass,Poison,318,45,49,49,65,65,45,1,False
2,Ivysaur,Grass,Poison,405,60,62,63,80,80,60,1,False
3,Venusaur,Grass,Poison,525,80,82,83,100,100,80,1,False
4,Charmander,Fire,,309,39,52,43,60,50,65,1,False
5,Charmeleon,Fire,,405,58,64,58,80,65,80,1,False
6,Charizard,Fire,Flying,534,78,84,78,109,85,100,1,False
7,Squirtle,Water,,314,44,48,65,50,64,43,1,False
8,Wartortle,Water,,405,59,63,80,65,80,58,1,False
9,Blastoise,Water,,530,79,83,100,85,105,78,1,False
10,Caterpie,Bug,,195,45,30,35,20,20,45,1,False
11,Metapod,Bug,,205,50,20,55,25,25,30,1,False
12,Butterfree,Bug,Flying,395,60,45,50,90,80,70,1,False
13,Weedle,Bug,Poison,195,40,35,30,20,20,50,1,False
14,Kakuna,Bug,Poison,205,45,25,50,25,25,35,1,False
15,Beedrill,Bug,Poison,395,65,90,40,45,80,75,1,False
16,Pidgey,Normal,Flying,251,40,45,40,35,35,56,1,False
17,Pidgeotto,Normal,Flying,349,63,60,55,50,50,71,1,False
18,Pidgeot,Normal,Flying,479,83,80,75,70,70,101,1,False
19,Rattata,Normal,,253,30,56,35,25,35,72,1,False
"""

הפעם נכתוב אותו לתוך קובץ בתסדיר CSV.
כדי לכתוב לקובץ נשתמש בפעולה write, לאחר שנפתח את הקובץ במצב כתיבה (w).

זהירות! פתיחת קובץ קיים במצב w תמחק את התוכן שלו מיידית.

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


In [ ]:
pokemons_file = open('pokemon.csv', 'w')

In [ ]:
# נסיר את השורות הריקות והרווחים מהצדדים
clear_pokemon = pokemons.strip()
# ונכתוב לקובץ
pokemons_file.write(clear_pokemon)

המספר שפייתון מחזירה הוא כמות התווים שייכתבו לקובץ.

אם תחפשו את הקובץ במחשב ותפתחו אותו, תגלו שפייתון עדיין לא כתבה אליו את הנתונים.
פייתון שומרת את הנתונים שביקשתם לכתוב בצד במנגנון זיכרון זמני שנקרא buffer ("מִכְלָא" בעברית, תודה ששאלתם), ותכתוב אותם לקובץ כשתסגרו אותו.
תוכלו להכריח את פייתון לכתוב לקובץ עוד לפני שסגרתם אותו באמצעות הפעולה flush:


In [ ]:
pokemons_file.flush()

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


In [ ]:
pokemons_file.close()

כשאתם בתוך המחברת, ניתן להשתמש בסימן קריאה כדי להריץ פקודות או תוכניות שלא קשורות לפייתון.
לדוגמה, הפקודה: !pokemon.csv תריץ את הקובץ שכתבנו.
שימו לב שלא תוכלו להריץ שום דבר אחר במחברת עד שתסגרו את הקובץ.


In [ ]:
!pokemon.csv

הוספה לקובץ

ניתן לפתוח קובץ במצב 'w' אם נרצה לכתוב לקובץ חדש, או לדרוס קובץ קיים.
אם נרצה להוסיף שורות לקובץ קיים, נפתח את הקובץ במצב ההוספה 'a', שמסמן append.

נוסיף את פוקימון מספר 20, רטיקייט, לקובץ הפוקימונים:


In [ ]:
new_line = "\n20,Raticate,Normal,,413,55,81,60,50,70,97,1,False"

In [ ]:
pokemons_table = open('pokemon.csv', 'a')

הוספת תוכן לקובץ במצב הוספה תתבצע באמצעות הפעולה write, בדיוק כמו בכתיבת קובץ חדש:


In [ ]:
pokemons_table.write(new_line)

In [ ]:
pokemons_table.close()

with

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


In [ ]:
passwords_file = open('resources/passwords.txt', 'r')
most_used_password = passwords_file.readline()
other_common_passwords = passwords_file.read()
passwords_file.close()

print(most_used_password.strip())

יכולה להיכתב אחרת בעזרת מילת המפתח with:


In [ ]:
with open('resources/passwords.txt', 'r') as passwords_file:
    most_used_password = passwords_file.readline()
    other_common_passwords = passwords_file.read()

print(most_used_password.strip())

שימו לב לצורת השימוש ב־with:

  1. כתבנו את מילת המפתח with.
  2. מייד לאחר מכן השתמשנו בפונקציה open כדי לפתוח את הקובץ עם הארגומנטים שרצינו.
  3. השתמשנו במילת המפתח as.
  4. הכנסנו את שם המשתנה שרצינו שבו ישמר ה־file handler.
  5. כתבנו נקודתיים כדי לסיים את השורה.
  6. השתמשנו בהזחה, ואז כתבנו את הפעולות שאנחנו רוצים לעשות בהקשר לקובץ.

בתור מתכנתים, אתם בוחרים איך לכתוב את הקוד שלכם.
ובכל זאת, קל לראות את היתרונות של שימוש ב־with: הקוד נעשה קריא ומסודר יותר, והקובץ נסגר לבד כשמסתיימת ההזחה.

כתיבת קוד עם with היא פשוטה יותר ממה שנראה בהתחלה –
5 הסעיפים הראשונים התייחסו לשורה הראשונה, ששקולה לשורה הראשונה והרביעית בקוד בלי ה־with.

מונחים

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

תרגולים

מי דגנים בריאים?

בקובץ cereal.csv שנמצא בתיקיית resources, ישנו מידע תזונתי על מִדְגַּנִּים שונים (יענו, דגני בוקר).
ככל שהמדגנים שמופיעים בשורה מסוימת בריאים יותר, כך המספר שמופיע לידם בעמודה rating גבוה יותר.
מצאו את המדגנים הבריאים ביותר והדפיסו את שמם לצד הציון שקיבלו.
קרדיט: את הקובץ הבאנו מכאן.

תקווה מארחת

בקובץ hope.txt שנמצא בתיקיית resources, נמצאת אחת הפואמות המהממות של אמילי דיקנסון, תִּקְוָה הִיא בַּעֲלַת-הַנוֹצָה.
אך אבוי! הפואמה התבלגנה, וכעת סדר המילים בכל שורה הוא הפוך.

במקום:

תִּקְוָה הִיא בַּעֲלַת-הַנוֹצָה
זוּ בַּנְּשָׁמָה תִשְׁכֹּן –

מופיע:

בַּעֲלַת-הַנוֹצָה הִיא תִּקְוָה
– תִשְׁכֹּן בַּנְּשָׁמָה זוּ

שמרו גיבוי של הקובץ, וכתבו קוד שמסדר את הפואמה המבולגנת.
שמרו את הפואמה המסודרת בקובץ חדש ששמו hope2.txt.

זהירות! יש פה בעיה שלא למדנו איך לתקן.
התרגיל בודק גם מה הבנתם מהשיעור על משאבים ברשת בשבוע שעבר :)