קובץ הוא מבנה שמאגד בתוכו נתונים השמורים במחשב שלכם.
לכל קובץ יש שם וכתובת (נתיב, או באנגלית 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 שמייצג קוד שבעזרתו המחשב יודע להציג דף אינטרנט.
המשותף לקבצים האלו הוא שאם ממירים את הביטים שמהם הם מורכבים לתווים, מתקבלת תוצאה שנוח לבני אדם לקרוא, ולא רק למחשב.
ישנם גם סוגי קבצים אחרים שלא נועדו לקריאה על־ידי עין אנושית.
קבצים כאלו נקראים "קבצים בינאריים", ונלמד לטפל בחלק מהם בשלב מתקדם יותר בקורס.
לפניכם דוגמה לכמה סוגי קבצים בינאריים:
בהמשך החוברת הזו נלמד לטפל בקבצים טקסטואליים.
אם בעבר השתמשנו בפנקס כדי לשמור מלל, הרי שכיום שימוש בקבצים ממוחשבים עושה הכול לקל ומהיר.
על קבצים ממוחשבים אפשר לבצע חישובים מסובכים בתוך חלקיק שנייה, ויתרון זה מעניק לנו יכולות שלא היו קיימות בעבר.
קבצים הם מקור מצוין לקלט ולפלט עבור התוכניות שאנחנו כותבים.
כמקור קלט, הם יכולים לכלול שורות רבות או מידע מורכב מהרגיל.
כפלט, הם מאפשרים לנו לשמור מידע בין הרצה להרצה, להעביר את המידע ממקום למקום בקלות ולייצג מידע מורכב בפשטות.
בקובץ passwords.txt שנמצא בתוך תיקיית resources, אספנו לכם את 25 הסיסמאות הנפוצות ביותר בעולם.
בתור התחלה, ננסה להציץ במה שכתוב בתוך הקובץ בעזרת פייתון.
הפונקציה open מאפשרת לנו לפתוח קובץ בעזרת פייתון, כדי להשתמש בו בהמשך התוכנית.
היא מקבלת 2 פרמטרים: הראשון הוא הנתיב לקובץ, והשני הוא צורת הגישה לקובץ, שעליה מייד נסביר.
הפונקציה מחזירה ערך שנקרא File handler, מעין מצביע על הקובץ שעליו נוכל לבצע פעולות.
צורת הגישה לקובץ תיבחר לפי המטרה שלשמה אנחנו פותחים אותו:
הפרמטר השני, צורת הגישה לקובץ, הוא מחרוזת.
טעות נפוצה היא לשים שם 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 ונבקש ממנה להחזיר את הסמן כך שיצביע למקום 0 – לפני התו הראשון:
In [ ]:
common_passwords_file.seek(0)
In [ ]:
print(common_passwords_file.read())
ניתן לראות איפה הסמן נמצא באמצעות הפעולה 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 מאפשרת לנו לאגד כמה שורות באותו הקשר.
בהקשר של קבצים, היא מאפשרת לנו לאגד תחתיה שורות שמטרתן טיפול בקובץ מסוים.
לדוגמה, השורות הבאות:
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:
with.open כדי לפתוח את הקובץ עם הארגומנטים שרצינו.as.
בתור מתכנתים, אתם בוחרים איך לכתוב את הקוד שלכם.
ובכל זאת, קל לראות את היתרונות של שימוש ב־with: הקוד נעשה קריא ומסודר יותר, והקובץ נסגר לבד כשמסתיימת ההזחה.
כתיבת קוד עם with היא פשוטה יותר ממה שנראה בהתחלה –
5 הסעיפים הראשונים התייחסו לשורה הראשונה, ששקולה לשורה הראשונה והרביעית בקוד בלי ה־with.
בקובץ cereal.csv שנמצא בתיקיית resources, ישנו מידע תזונתי על מִדְגַּנִּים שונים (יענו, דגני בוקר).
ככל שהמדגנים שמופיעים בשורה מסוימת בריאים יותר, כך המספר שמופיע לידם בעמודה rating גבוה יותר.
מצאו את המדגנים הבריאים ביותר והדפיסו את שמם לצד הציון שקיבלו.
קרדיט: את הקובץ הבאנו מכאן.
בקובץ hope.txt שנמצא בתיקיית resources, נמצאת אחת הפואמות המהממות של אמילי דיקנסון, תִּקְוָה הִיא בַּעֲלַת-הַנוֹצָה.
אך אבוי! הפואמה התבלגנה, וכעת סדר המילים בכל שורה הוא הפוך.
במקום:
תִּקְוָה הִיא בַּעֲלַת-הַנוֹצָה
זוּ בַּנְּשָׁמָה תִשְׁכֹּן –
מופיע:
בַּעֲלַת-הַנוֹצָה הִיא תִּקְוָה
– תִשְׁכֹּן בַּנְּשָׁמָה זוּ
שמרו גיבוי של הקובץ, וכתבו קוד שמסדר את הפואמה המבולגנת.
שמרו את הפואמה המסודרת בקובץ חדש ששמו hope2.txt.
זהירות! יש פה בעיה שלא למדנו איך לתקן.
התרגיל בודק גם מה הבנתם מהשיעור על משאבים ברשת בשבוע שעבר :)