תוכן עניינים הקדמה
הכרות עם schemes
תבניות נתונים
תבניות
משפטי בקרה
משתנים לקסיקלים
רקורסיה
קלט/פלט
macros
מבנים
alises and tables ממשק המערכת
מחלקות ואובייקטים
jumps
אי-דטרמיניסטיות
מנועים
shell scripts
אתר ללימוד מקיף> פרק 7 - קלט/ פלט

פרק 7 - קלט/ פלט


הנושאים בפרק זה:
7.1.     קריאה
7.2.     כתיבה
7.3.     File ports
          7.3.1. פתיחה וקריאה אוטומטית של File ports
7.4.     String ports
7.5.     טעינת קבצים


ל-Scheme פרוצדורות קלט/ פלט המאפשרות לקרוא מ-port המשמש לקלט או לכתוב ל-port המשמש לפלט.
ports יכולים להיות מקושרים ל-console לקבצים או למחרוזות.

7.1 קריאה

פרוצדורות הקריאה ב-Scheme מקבלות ארגומנט port קלט אופציונלי. אם ה- port אינו מצוין בפירוש - מניחים שהכוונה היא ל-port הקלט הנוכחי (בד"כ ה-console).
קריאה יכולה להיות על בסיס תו, שורה או ביטוי. בכל פעם שמתבצעת קריאה, מצב ה-port משתנה כך שבקריאה הבאה ייקרא חומר עוקב לחומר שכבר נקרא. אם ל-port אין יותר חומר לקרוא, פרוצדורת הקריאה מחזירה נתון מסוים הנקרא סוף-הקובץ (end-of-file) או אובייקט eof. נתון זה הוא הערך היחיד המספק את פרדיקט ?eof-object.

הפרוצדורה read-char קוראת את התו הבא מה-port.
read-line קוראת את השורה הבאה ומחזירה אותה כמחרוזת. (ירידת השורה האחרונה אינה נכללת).
הפרוצדורה read קוראת את הביטוי הבא.



לתחילת העמוד

7.2 כתיבה

פרוצדורות הכתיבה ב-Scheme מקבלות את האובייקט שאמור להיכתב ואת ארגומנט port הפלט האופציונלי. אם ה-port אינו מצוין במפורש - מניחים שהכוונה ל-port הפלט הנוכחי. (בד"כ ה-console).
כתיבה יכולה להיעשות על בסיס תו או ביטוי.
הפרוצדורה write-char כותבת את התו הנתון (ללא #/) ל-port.
הפרוצדורות write ו-display כותבות את הביטוי הנתון ל-port בהבדל אחד:
write מנסה להשתמש במבנה מנגנון קריאה בעוד ש-display אינה עושה זאת.
לדוגמא, write משתמשת במרכאות כפולות עבור מחרוזות ובתחביר ה-#/ עבור תווים. display אינה עושה זאת.



לתחילת העמוד

7.3 File ports


פרוצדורות קלט/ פלט ב- scheme לא נזקקות לארגומנט ה- port אם אותו port הוא אמצעי הקלט/הפלט הסטנדרטי. בכל מקרה, אם צריכים portים אלו במפורש - הפרוצדורות ללא ארגומנטים current-input-port ו-current-output-port מספקות אותם. לפיכך, בשתי השורות הבאות קורה אותו הדבר בדיוק.

(display 9)
(display 9 (current-output-port))

Port מקושר עם קובץ באמצעות פתיחת הקובץ. הפרוצדורה open-input-file מקבלת ארגומנט שם קובץ ומחזירה port קלט חדש המקושר איתו. הפרוצדורה open-output-file מקבלת ארגומנט שם קובץ ומחזירה port פלט חדש המקושר איתו.
זו נחשבת טעות לפתוח קובץ קלט שאינו קיים או לפתוח קובץ פלט שכבר קיים.
אחרי שביצענו קלט/פלט על ה-port, עלינו לסגור אותו בעזרת close-input-port או
close-output-port.
בדוגמא הבאה, הנח כי הקובץ hello.txt מכיל את המילה הבודדת hello.

(define i (open-input-file "hello.txt"))
(read-char i)
=> #\h
(define j (read i))
j
=> ello

הנח כי הקובץ greeting.txt אינו קיים לפני שהתכניות הבאות מוזנות ל-listener.

(define o (open-output-file "greeting.txt"))

(display "hello" o)
(write-char #\space o)
(display 'world o)
(newline o)

(close-output-port o)

הקובץ greeting.txt יכיל כעת את השורה:

hello world




לתחילת העמוד

7.3.1 פתיחה וסגירה אוטומטית של file ports

Scheme מספקת את הפרוצדורות call-with-input-file ו call-with-output-file השומרות על פתיחת ה-port וסגירתו לאחר שסיימת לעבוד איתו.
הפרוצדורה call-with-input-file מקבלת ארגומנט שם קובץ ופרוצדורה. הפרוצדורה מתייחסת ל-port קלט הנפתח בקובץ. כשהפרוצדורה מסיימת, תוצאותיה מוחזרות לאחר וידוא שה- port אכן סגור.

(call-with-input-file "hello.txt"
  (lambda (i)
    (let* ((a (read-char i))
           (b (read-char i))
           (c (read-char i)))
      (list a b c))))
=> (#\h #\e #\l)

הפרוצדורה call-with-output-file מעניקה את השימושים האנלוגיים עבור קובץ פלט.



לתחילת העמוד

7.4 String ports


לעיתים נוח לקשר ports עם מחרוזות. לפיכך, הפרוצדורה open-input-string נועדה לקשר port עם מחרוזת נתונה. פרוצדורות קריאה ב-port זה יקראו את המחרוזת:

(define i (open-input-string "hello world"))

(read-char i)
=> #\h

(read i)
=> ello

(read i)
=> world

הפרוצדורה open-output-string יוצרת port פלט שלבסוף ינוצל ליצירת מחרוזת:

(define o (open-output-string))

(write 'hello o)
(write-char #\, o)
(display " " o)
(display "world" o)

כעת תוכל להשתמש בפרוצדורה get-output-string כדי לקבל את המחרוזת שהצטברה ב-string port בשם o:

(get-output-string o)
=> "hello, world"

String ports לא חייבים להסגר במפורש.



לתחילת העמוד

7.5 טעינת קבצים


קודם ראינו את הפרוצדורה load הטוענת קבצים המכילים קוד Scheme. טעינת קובץ מבוססת על הערכה סדרתית של כל תבנית Scheme בקובץ. ארגומנט ה-pathname הניתן ל-load מוערך ביחס לקרבתו לספריית הפעולה הנוכחית של Scheme, שהיא בד"כ הספרייה שבה הקריאה לריצה נעשתה.

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

MzScheme מספקת את פרוצדורת load-relative שעוזרת במידה רבה בייצוב הקבצים לצורך הטענתם. load-relative, כמו load, מקבלת את ארגומנט pathname. כשמתרחשת קריאת load-relative בקובץ foo.scm, מסלול הארגומנטים שלה מחושב מהספרייה של הקובץ הקורא foo.scm .בייחוד, pathname זה מחושב באופן בלתי תלוי בספריית Scheme הנוכחית, ובכך מאפשר פיתוח נוח של תכנית רבת קבצים.