מצב מטפל קובץ והדפסה#

שבעה משתנים שמניעים את ה־I/O של Perl. הם גלובליים - קביעת $/ בשגרה אחת משנה כיצד כל שגרה אחרת באותו תהליך קוראת ממטפל קובץ. המשמעת שמונעת מכך להיות אסון היא ביצוע local: כל שינוי במשתנה בעמוד זה צריך להיות בתוך בלוק local אלא אם התוכנית באמת רוצה שהערך החדש יהיה קבוע.

משתנה

נקרא

ברירת מחדל

משמש את

$/

מפריד רשומת קלט

"\n"

<$fh>, readline

$\

מפריד רשומת פלט

undef

print, printf, say (\n כפוי)

$,

מפריד שדה פלט

undef

print, printf (בין ארגומנטים)

$"

מפריד רשימה

" "

שיבוץ מערך "@arr"

`$

`

autoflush פלט

0

$.

מספר השורה הנוכחי

0

מטפל הקובץ האחרון שנקרא

$;

מפריד מנויים

"\034"

אמולציית רב־ממדיות $h{$x, $y}

$/ - מפריד רשומת הקלט#

שולט במה ש־<$fh> ו־readline מתייחסים אליו כסוף רשומה אחת. ברירת המחדל "\n" משמעותה קריאה שורה־אחר־שורה; קביעתו לערכים אחרים מפיקה מצבי קריאה אחרים.

מצב שורה (ברירת המחדל)#

while (my $line = <$fh>) {
    chomp $line;                 # strips the terminator $/
    process($line);
}

chomp מסיר את כל מה ש־$/ מחזיק כרגע - כשאתם משנים את $/, chomp עוקב אחריו.

מצב slurp#

undef $/ גורם ל־<> לקרוא את הקובץ כולו בבת אחת:

my $whole_file = do {
    local $/;
    <$fh>;
};

ה־local חיוני. בלעדיו, כל חתיכת קוד אחרת שקוראת לאחר מכן ממטפל קובץ באותו תהליך תבצע slurp בשקט.

מצב פסקה#

local $/ = ""; קורא פסקה־בכל־פעם, ומפצל על רצפים של שתי שורות חדשות או יותר:

{
    local $/ = "";
    while (my $para = <$fh>) {
        # $para ends with exactly two newlines (or one at EOF)
        process_paragraph($para);
    }
}

זוהי התנהגות ה־RS="" של awk. שורות ריקות מובילות בתחילת הקובץ מדולגות בשקט.

מצב רשומה־קבועה#

local $/ = \N קורא N בתים בכל קריאה:

local $/ = \4096;                # binary 4-KiB chunks
while (defined(my $chunk = <$fh>)) {
    process($chunk);
}

ההפניה היא למספר, לא למספר עצמו. זוהי הצורה הנכונה לקריאת נתונים בינאריים, מנות רשת, או ביצוע העתקה בכמות מבלי לסרוק עבור \n.

מסיימים רב־תוויים#

local $/ = "\r\n";               # CRLF mode
local $/ = "\nEND-OF-RECORD\n";  # custom marker

המסיים הוא מחרוזת מילולית, לא regex.

$\ - מפריד רשומת הפלט#

מצורף לכל קריאת print. ברירת המחדל היא undef (לא מצורף דבר). קביעתו משנה כל print הבא:

{
    local $\ = "\n";             # all prints get a trailing newline
    print "first";
    print "second";
}
# Output: "first\nsecond\n"

זוהי ההתאמה הטבעית עבור -l בשורת הפקודה - -l קובע את $\ ל־\n. בקוד סקריפט, local $\ = "\n" בתוך לולאת דיווח צפופה שווה לעיתים; אחרת say (שכופה \n ללא קשר ל־$\) הוא הכתיב הנקי יותר.

$, - מפריד שדה הפלט#

מוכנס בין הארגומנטים של print. ברירת המחדל היא undef:

print "a", "b", "c";              # "abc"

{
    local $, = ", ";
    print "a", "b", "c";          # "a, b, c"
}

{
    local $, = "\t";
    print @items;                 # tab-separated record
}

$, הוא לכל־print, לא לכל־שורה - הוא אינו מפריד בין קריאות print עוקבות. שלבו עם $\ עבור פלט מלא בסגנון TSV:

local ($,, $\) = ("\t", "\n");
print 'col1', 'col2', 'col3';     # "col1\tcol2\tcol3\n"

$" - מפריד הרשימה#

מאחד אלמנטים של מערך כאשר מערך משובץ לתוך מחרוזת במרכאות כפולות. ברירת המחדל היא " " (רווח יחיד):

my @arr = (1, 2, 3);
print "items: @arr\n";            # "items: 1 2 3"

{
    local $" = ', ';
    print "items: @arr\n";        # "items: 1, 2, 3"
}

{
    local $" = "\n  ";
    print "items:\n  @arr\n";     # "items:\n  1\n  2\n  3"
}

$" רק מתייעצים בו בשיבוץ. print @arr (ללא מרכאות) משתמש ב־$,, לא ב־$".

$| - autoflush פלט#

כופה flush לאחר כל כתיבה על מטפל הקובץ הנבחר כרגע:

$| = 1;                          # autoflush STDOUT
print "Loading..."; sleep 1;     # appears immediately, not at exit

הסיוג ”הנבחר כרגע“ חשוב. כברירת מחדל, select מחזיר STDOUT, ולכן $| = 1 מבצע autoflush ל־STDOUT. כדי לבצע autoflush למטפל אחר:

my $old = select($log_fh);
$| = 1;
select($old);                    # restore selection

הכתיב המודרני הנחמד מעט יותר משתמש ב־IO::Handle:

$log_fh->autoflush(1);

$| נקרא־כבוליאני: print $| מציג 1 או 0.

$. - מספר השורה הנוכחי#

מספר השורה של הקריאה האחרונה בסגנון <>. לכל מטפל קובץ יש מונה משלו; קריאה ממטפל אחר מכנה את $. למונה של אותו מטפל:

while (<$fh>) {
    print "$.: $_";              # numbered listing
}

# After the loop, $. is the total number of lines read.

$. מאופס כאשר מטפל הקובץ נסגר. פתיחה מחדש (ללא סגירה מפורשת בינתיים) אינה מאפסת.

עבור איטרציה רב־קבצית דרך <>, השורות ממוספרות באופן מצטבר על פני הקבצים אלא אם תסגרו במפורש את ARGV בכל גבול קובץ - ראו eof עבור הניב התקני close ARGV if eof;.

$; - מפריד המנויים#

משמש לזיוף האשים רב־ממדיים - תכונת מורשת עמוקה שנשמרה לתאימות לאחור:

$h{$x, $y} = $value;             # equivalent to $h{"$x\034$y"}

ערך ברירת המחדל הוא "\034" (FS, ”file separator“, בית בקרה ASCII עמום). מבנים רב־ממדיים אמיתיים צריכים להשתמש בהפניות האש מקוננות ($h{$x}{$y}); $; קיים משום של־Perl 4 לא היו הפניות כראוי.

מתי באמת מבצעים local לאלה#

הצורה הנפוצה ביותר, בפער ניכר:

sub slurp_text {
    my ($path) = @_;
    open my $fh, '<', $path or die "open $path: $!";
    local $/;                    # slurp inside this sub only
    return <$fh>;
}

sub paragraphs_of {
    my ($path) = @_;
    open my $fh, '<', $path or die "open $path: $!";
    local $/ = "";
    return <$fh>;                # list context: list of paragraphs
}

sub csv_print {
    my (@row) = @_;
    local ($,, $\) = (",", "\n");
    print @row;
}

המחיר של שכחת local בלתי־נראה בזמן הכתיבה וקטסטרופלי בזמן השימוש - תת־שגרה ש“רק קובעת $/“ יכולה לשבור קוד לא־קשור שקורא ממטפלי קובץ שונים לחלוטין. התייחסו אל $/, $\, $,, $" כדגלי מצב גלובליים: קבעו אותם תמיד עם local.

ראו גם#

  • open, readline, <$fh> - הצרכנים של $/.

  • print, printf, say - הצרכנים של $\, $,.

  • chomp - מסיר את כל מה ש־$/ מחזיק כרגע; מזווג עם שינויי $/.

  • eof - הניב close ARGV if eof שגורם ל־$. לספור לכל־קובץ על פני <>.

  • select - בוחר את מטפל הקובץ שעליו חלים $| וה־$,/$\ ללא קידומת.

  • IO::Handle - מספק את autoflush, output_record_separator, וכו« כקריאות מתודה.

  • local - הדרך הבטוחה לשנות כל משתנה בעמוד זה.

  • שיבוץ - כיצד $" מעצב הרחבת מערך בתוך מחרוזות במרכאות כפולות.