קבצים תיקיות ופייתון


קבצים ותיקיות

ניווט בין קבצים

הבסיס

לפני שנתעסק בקבצים, קודם כל נלמד איך להתמצא בתיקיות המחשב עם פייתון. ראשית, נייבא את ספריית הos על ידי import os. על מנת לשלוף את כתובת התיקייה בה קובץ הפייתון שלנו (למשל: D:\Main files\Coding\Phyton Training\Junk), נבצע os.getcwd() כך שפירוש cwd הוא בעצם current working directory. כלומר, המיקום בתיקיות שכעת אנו עובדים עליו. בדרך כלל קבצים שנרצא לקרוא יקראו ממיקום זה, וקבצים שנרצה לשמור יישמרו במיקום. במידה ונרצה לשנות אותו נשתמש בפקודת chdir -

import os
____
os.chdir('..')
os.chdir('SomeFolder')
os.chdir('D:\Main files\Coding')

לפעולה זו נוכל להזין את מיקום התיקייה בשלושה דרכים מרכזיות, ראשית, במידה ונרצה לנווט לתיקייה אחת אחורה נוכללהשתמש ב'..' שנית, במידה ונרצה להיכנס לתיקייה שנמצאת בתוך תיקיית העבודה שלנו, נוכל לעשות זאת בקלות על ידי הזנת שם התייקיה, 'SomeFolder' או שלישית, נוכל להזין את כל כתובת התיקייה, לדוגמא 'D:\Main files\Coding'

מה עוד נוכל לעשות בכתובת מסויימת? ראשית, נוכל לקבל רשימה של כל הפרטים בה (קבצים ותיקיות). על ידי שימוש ב os.listdir(). אבל מה אם תרצו ללכת עמוק יותר? לכך נשתמש בos.walk(), פעולה שגם תכנס לכל התיקיות והתיקיות בתוך התיקיות והלאה שמתחת לכתובת שסיפקתם. נשתמש בה כך –

import os
____
for dirpath,dirs,files in os.walk(os.getcwd()):
____print('path - ' , dirpath)
____print('directories - ' , dirs)
____print('files - ' , files)

הפעולה הזאת בעצם תעבור בכל התיקיות בתוך הcwd (כולל), ותרשום ראשית את הכתובת שהיא נמצאת בה כעת, לאחר מכן את שמות התיקיות, ואז שמות הקבצים באותה הכתובת. (dirs, files הינם רשימות).

יצירת תיקיות

על מנת ליצור תיקייה נשתמש בפקודת הos.mkdir() היכולה ליצור תיקייה אחת, או בos.makedirs() המסוגלת ליצור שרשרת תיקיות אחת בתוך השנייה. כך -

import os
____
os.mkdir('hello') // יוצר את תיקיית הלו
os.makedirs('its/me/noder') // יוצר גם את תיקיית יטס ואז את תיקיית מי ואז את נודר
if (os.path.exists(os.getcwd() + "/hello")):
____os.mkdir('hello/its me') // ייצור את איטס מי בתוך הלו

תתכוננו לקצת סיבוך. הפעולה הראשונה ותיצור את תיקיית hello, בתוך הcwd, הפעולה השנייה תיצור בתוך הcwd את its ובתוכה את me ובתוכה את noder. הפעולה השלישית תנסה ליצור את its me בתוך hello, אבל, במידה וhello לא קיימת כבר, תוחזר שגיאה, שכן הפעולה לא יוצרת תיקיות בשרשור. לכן, יצרתי את התנאי הבודק לפני הביצוע אם hello קיימת, וזאת בעזרת הפעולה os.path.exists() הבודקת אם כתובת כלשהי קיימת.

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

import os
____
os.rmdir('hello/its me') //ימחק את תיקיית יטס מי בלבד, שבתוך תיקיית הלו
os.removedirs('its/me/noder') //ימחק את שלושת התיקיות
os.rmdir('hello') //מוחק את תיקיית הלו
so.remove('bribra.txt') //מוחק את הקובץ בריברה

כך ש rmdir מקביל לmkdir, וremovedirs לmakedirs. במחיקה נשים לב שאנו מוחקים תיקייה שקיימת, ושהיא ריקה. למחיקת קבצים, נשתמש בפעולה האחרונה, os.remove() המשמשת למחיקת קבצים, ולא ניתן להשתמש בה על תיקיות.

שינוי שמות

דרך פייתון ניתן לשנות שמות של תיקיות או קבצים הנמצאים בcwd, נעשה זאת על ידי שימוש בפקודת הos.rename(), כך –

os.rename('hello', 'goodbye')
os.rename('bruh.txt', 'newname.txt')

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

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

import os
____
for dirpath,dirs,files in os.walk(os.getcwd(), topdown=False):
____os.chdir(str(dirpath))
____i = 0
____for dir in dirs:
________i += 1
________os.rename(str(dir),'mynemmajeff' + str(i))
____for file in files:
________i += 1
________siyomet = str(file).split('.')[1]
________os.rename(str(file),'mynemmajeff' + str(i) + "." + siyomet)

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

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

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

פרטי קבצים

נוכל לבקש מהתוכנה לתת לנו פרטים בנוגע לקובץ מסויים בעזרת הפקודה os.stat, כך –

import os
____
print(os.stat('theJeffer.py'))

והפלט שנקבל יהיה –

os.stat_result(st_mode=33206, st_ino=1125899906848154, st_dev=3523251319, st_nlink=1, st_uid=0, st_gid=0, st_size=356, st_atime=1586710655, st_mtime=1586705449, st_ctime=1586703260)

כלומר, יוחזרו לנו הרבה פרטים שונים. כמה פרטים שימושיים הם,

  • st_ctime – זמן יצירת הקובץ (בפורמט timestamp)
  • st_mtime – זמן השינוי האחרון של הקובץ (בפורמט timestamp)
  • st_size – הגודל של הקובץ בביתים.

ניגש לאחד ספציפי כך –

import os
from datetime import datetime
____
creation_time = os.stat('theJeffer.py').st_ctime
print(datetime.fromtimestamp(creation_time))

בקוד הנ"ל גם ביצעתי העברה מtimestamp לפורמט זמן קריא.

כתובות

מה נעשה אם קובץ הפייתון שלנו יהיה בתיקיית home אבל בתוך התיקייה ישנה עוד תיקייה שקוראים לה room, ונרצה את כתובת התיקייה room על מנת להגדיר אותה כcwd? נשתמש בפעולה os.path.joim(),התחבר לנו עוד חלקים לכתובת כך -

import os
____
path = os.path.join(os.getcwd(), 'room')
os.chdir(path)

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

במידה ונרצה לבדוק אם כתובת מסויימת מובילה לתיקייה או לקובץ, נוכל להשתמש בפעולות os.path.isdir(), os.path.isfile(). נכניס אליהם כתובת ונקבל תשובה בוליאנית.


קבצים


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

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

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



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

פתיחה וקריאה

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

file = open('mikmakcheats.txt', mode = 'r')
print('opened')
file.close()
____
with open('mikmakcheats.txt', mode = 'r') as file:
____print('opened')

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

  • r - קריאה כטקסטואלי, נשתמש כנרצה לקרוא קובץ בשם מסויים.
  • rb - קריאה כבינארי.
  • w - כתיבה כטקסטואלי, נשתמש כנרצה לשמור משהו כקובץ בשם מסויים. אם קיים כבר קובץ בשם זה הוא ישוכתב, ואם לא יווצר קובץ באותו השם.
  • wb - כתיבה כבינארי.
  • a - הוספה כטקסטואלי, נשתמש כשנרצה להוסיף עוד מידע מחדש לקובץ. למשל אם יש לנו קובץ המתעד פעולות שנעשו, ונרצה להוסיף לתעד בו פעולה חדשה (לוגים). אם לא קיים כבר קובץ בשם זה הוא יווצר.
  • ab - הוספה כבינארי.
  • r+ - כתיבה ועריכה כטקסטואלי.
  • r+b - כתיבה ועריכה כבינארי.

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

with open('mikmakcheats.txt' , 'r') as file:
____filetext = file.read()
____print(filetext)

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

with open('mikmakcheats.txt' , 'r') as file:
____filetext = file.read(100)
____while len(filetext) > 0:
________print(filetext, end = '')
________filetext = file.read(100)

ראשית, נקראים 100 התווים הראשונים, לאחר מכן נכנס ללולאה שנמשכת כל עוד המשתנה בו מאוחסנים הערכים המוצאים לא ריק. בתוך הלולאה יודפסו הערכים שהוצאו, ואז יובאו ה100 הבאים. חדי העין יראו כי בפעולת ההדפסה הגדרתי שend='', בעצם פעולת הprint() מכניסה ערך נוסף בסוף כל הדפסת שורה. בברירת המחדל זהו סימן השורה החדשה, מה שאומר שכל הדפסה תהיה בשורה נפרדת. אני שיניתי אותו לבעצם כלום שכן להדפסה השורה חדשה אין הגיון במקרה זה.

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

כתיבה

על מנת לפתוח קובץ מפורמט טקסטואלית במצב כתיבה נבצע –

with open('mikmakcheats.txt' , 'w') as file:
____file.write('minecraft > fortnite')

הפעולה file.write() תכתוב את הטקסט שנגדיר. רק נזכור שבמידה ונבצע פעולה זו, אם היה משהו בקובץ לפני פתיחתו הוא ימחק ויוחלף במה שרשמנו. אם לא נרצה שזה יקרה, נשקול להשתמש בa או r+ או בשיטת עבודה אחרת, נפתח את הקובץ לקריאה, נשמור אותו במשתנה, נעשה את פעולות העריכה שנרצה על המשתנה, ובסוף העריכה נשמור את המידע במשתנה בתור אותו הקובץ. או אם נרצה לשמר את המקור, בשם אחר.

כתיבה קריאה ובינארי

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

with open('Darude_Sandstorm.mp3' , 'rb') as fileoriginal:
____with open('Darude_Sandstorm_copy.mp3' , 'wb') as filecopy:
________filedata = fileoriginal.read(100)
________while len(filedata) > 0:
____________filecopy.write(filedata)
____________filedata = fileoriginal.read(100)

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

ניסויים

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



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

אתם מוזמנים לנסות להתעלל בקבצים גם בבית!


בונוס: על המחשב

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

  • platform.node() - שם המחשב
  • platform.processor() - שם המעבד
  • platform.system() - סוג מערכת ההפעלה
  • platform.version() - גרסת מערכת ההפעלה
  • os.getlogin() - בנוסף, בעזרת סיפריית הos נוכל לקבל את שם היוזר הנוכחי.



אין תגובות:

הוסף רשומת תגובה