פרק 6: טיפול בקבצים

[הקודם : פונקציות והוראות][חזרה לתוכן העניינים][הבא : מודולים]

6.1 הכוונת קלט\פלט

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

./myprog.pl <inFile >outFile

התוכנית תקבל את הקלט מתוך הקובץ inFile, ותוציא את הפלט לקובץ outFile. והיופי בזה, שהתוכנית בכלל לא יודע שהיא עושה את זה. מבחינתה, היא מקבלת הקלדות מהמשתמש, ומוציאה לו על המסך מידע.
אתה יכול לקחת את תוכנית הדוגמא שבסוף הפרק, להוריד את כל הוראות פתיחת וסגירת הקבצים, ופשוט לחתוך את המילים "InFile" ו-"OutFile". ואם נקרא לתוכנית LinkProcess.pl, אזי אפשר להריץ:

./LinkProcess.pl  < urls.txt  > urls.html

6.2 משתנה מטיפוס קובץ

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

open(FH, "> wwwest.txt");

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

6.3 פתיחת קובץ

אז הנה, ככה פותחים קובץ:

open(my $fh, ">", "wwwest.txt");

הפקודה הזאת פותחת את הקובץ wwwest.txt לכתיבה. אם הקובץ לא קיים, הוא נוצר.
שים לב שהגדרנו את $fh בעזרת פקודת my, ולכן הוא משתנה מקומי כמו כל משתנה אחר.
בד"כ מוסיפים לפקודת open גם פקודת die:

open(my $fh, ">", "wwwest.txt") or die "Cann't open wwwest.txt!\n";

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

open(my $fh, "<", "wwwest.txt") or die "Can't open wwwest.txt!\n";

פתיחת הקובץ להוספה (Append):

open(my $fh, ">>", "wwwest.txt") or die "Can't open wwwest.txt!\n";

6.4 אופרטור <>

הזכרתי את האופרטור הזה בעבר. מה שהוא עושה, זה לקרוא מקובץ שורה. ככה:

$line = <$fh>;

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

my %config;
while (my $line = <$fh>) { 
    chomp $line; 
    next if ($line =~ m/^#/); # skip comments
    my ($key, $value) = split '=', $line;
    $config{$key} = $value;
    print "Got config: $key=$value\n";
}

פקודת ה-while מפעילה את אופרטור ה-<>, שקורא שורה מתוך מחזיק קובץ $fh שככל הנראה פתחנו קודם לקריאה, ומכניס את השורה (כולל סימן סוף השורה) לתוך המשתנה $line. כל עוד שיש מה לקרוא מהקובץ, המשתנה הוא אמת, ולולאת ה-while ממשיכה לרוץ.
שורה שניה - עושה chomp ל-$line - מה שמוריד את סימן סוף השורה מהמשתנה. כי הוא בד"כ לא באמת מעניין אותנו.
שורה שלישית - בודקת האם הקלט מתחיל בסימן '#'. אם כן - זוהי שורת הערה ומדלגים לשורה הבאה.
אנחנו יודעים שבקובץ יש שורות של פרמטרים, בפורמט של מפתח=ערך. אז שוברים את השורה לשניים לפי ה-"=". הראשון מפתח, השני ערך, ואנחנו מכניסים את שניהם להאש כלשהו. בתקווה שגם נעשה עם ההאש העם ההאש הזה משהו אח"כ.
אם תריץ את האופרטור <> בהקשר רשימה, אז הוא יקרא את כל השורות, ולא רק אחת.

my @lines; 
@lines=<>;

אחרי זה, כל שורות הקלט יהיו בתוך המערך @lines.

6.5 כתיבה לקובץ

פה אין הרבה הפתעות - בשביל לכתוב לקובץ משתמשים בפקודה print. כמו שעד עכשיו כתבנו לתוך הפלט הסטנדרטי, ככה נכתוב לכל קובץ פתוח אחר:

open(my $fh, ">", "tmp.txt") or die "AHHHHH!\n"; 
print $fh "The time is: " , scalar(localtime()), "\n";

שים לב שאין פסיק בין FH לבין המחרוזת הראשונה. ככה זה. (ואם תשים פסיק, זו תהיה טעות. פקודת ה-print תנסה להדפיס את מחזיק הקובץ. דווקא חוסר הפסיק אומר לו שפה יש מחזיק קובץ, ולא משהו להדפסה)

6.6 סגירת קובץ

בצורה מפתיעה ביותר, בשביל לסגור קובץ משתמשים בפקודת close.

close($fh);

6.7 אבל הקובץ שלי הוא

אבל הקובץ שלי הוא לא סתם שורות! הוא <הכנס כאן פורמט זה או אחר>!
ובכן, אם זה פורמט מוכר, רוב הסיכויים שיש כבר מודול ב-CPAN שמטפל בו. הצץ בפרק 7 להסברים על חיפוש והתקנת מודולים משם.
אם זה קובץ בינארי לא מוכר, ואין מודול שיעשה לך את העבודה, תצטרך להשתמש בפקודות binmode, read, write לקרוא ולכתוב מהקובץ הזה. הנה דוגמא של תוכנית קטנה שעוברת על קובץ בינארי ועושה checksum עליו:

open my $fh, "< :raw", "binfile.dat" or die "can not open file";
my $buf = '';
my $checksum = 0;
while (read( $fh, $buf, 100 )) {
    my @numbers = unpack "C*", $buf;
    for my $t (@numbers) {
        $checksum = ( $checksum + $t ) % 256;
    }    
}
print "File Checksum is $checksum\n";

6.8 תוכנית דוגמא

#!/usr/bin/perl -w 
use strict; 

open(my $InFile, "<", "urls.txt") or die "Error IN!\n"; 
open(my $OutFile, ">", "urls.html") or die "Error OUT\n"; 

print $OutFile "<HTML><HEAD><TITLE>"; 
print $OutFile "Semuel's Link Page"; 
print $OutFile "</TITLE></HEAD><BODY>"; 

while(my $line = <$InFile>){ 
    chomp $line; 
    if ($line=~m/^http/i) { 
        print $OutFile "<A HREF= $line > $line </A><br>\n"; 
    } else { 
        print $OutFile "$line <br>\n"; 
    } 
} 

print $OutFile"</BODY></HTML>"; 

close($InFile); 
close($OutFile);

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

[הקודם : פונקציות והוראות][חזרה לתוכן העניינים][הבא : מודולים]

נכתב ע"י שמואל פומברג, כל הזכויות שמורות © ראה פרק 1.5 לתנאי רשיון