• בלוג
  • למה צריך כפתור מיוחד כדי לעבוד עם קבצים בינאריים?

למה צריך כפתור מיוחד כדי לעבוד עם קבצים בינאריים?

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

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

import binascii

def write_stuff_to_file(fname):
    with open(fname, 'w') as f:
        f.write('\x10')

def read_stuff_from_file(fname):
    with open(fname, 'r') as f:
        stuff = f.read()
        print(binascii.hexlify(stuff.encode('utf-8')))

write_stuff_to_file('demo.txt')
read_stuff_from_file('demo.txt')

התוכנית כותבת את הביטים של המספר 10 לקובץ בשם demo.txt ואז קוראת מהקובץ את אותם ביטים ומדפיסה אותם על המסך. התוצאה:

b'10'

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

import binascii

def write_stuff_to_file(fname):
    with open(fname, 'w') as f:
        f.write('\x0d')

def read_stuff_from_file(fname):
    with open(fname, 'r') as f:
        stuff = f.read()
        print(binascii.hexlify(stuff.encode('utf-8')))

write_stuff_to_file('demo.txt')
read_stuff_from_file('demo.txt')

ועכשיו התוצאה:

b'0a'

מה קרה כאן? כתבתי לקובץ את המספר 13 וקיבלתי בחזרה את המספר 10. נלך לראות מה כתוב בקובץ:

$ xxd demo.txt 
00000000: 0d

לפחות הכתיבה עבדה כמו שצריך. בקובץ באמת כתוב המספר 13 (שמודפס בתור d בכתיב הקסדצימלי), אבל כשאנחנו קוראים את זה מתוך תוכנית פייתון אנחנו מקבלים את המספר 10. מסתבר שכשאתם קוראים קובץ בתור קובץ טקסט פייתון ינסה לתקן לכם את תווי ירידת השורה שבקובץ. התו שמיוצג על ידי המספר 13 נקרא CR ומשתמשים בו כשיורדים שורה על מכונות Windows. על מק תו ירידת השורה נקרא LF ומיוצג על ידי המספר 10, ולכן כשקוראים מקובץ טקסט את המספר 13 על מכונת מק פייתון מניח שאנחנו קוראים קובץ טקסט שמישהו כתב על Windows ולכן מחליף את תו ירידת השורה כדי שיסתדר עם מה שמתאים למק.

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

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

בחזרה לתוכנית הקודמת - שינוי הפקודה open לעבודה עם מידע בינארי יבטל את המרת תווי ירידת השורה ויגרום לתוכנית לעבוד ולקרוא מהקובץ בדיוק את אותו מידע שכתבנו:

import binascii

def write_stuff_to_file(fname):
    with open(fname, 'wb') as f:
        f.write(b'\x0d')

def read_stuff_from_file(fname):
    with open(fname, 'rb') as f:
        stuff = f.read()
        print(binascii.hexlify(stuff))

write_stuff_to_file('demo.txt')
read_stuff_from_file('demo.txt')

נ.ב. - לא כתבתי כאן אבל בכל עבודה עם קבצי טקסט בפייתון כדאי לציין באופן מפורש את הקידוד בו הקבצים נשמרו. זה אומר שקריאה אמיתית ל open נראית בדרך כלל כך:

fname = 'demo.txt'
with open(fname, 'r', encoding='utf-8') as f:
    pass