sysseek#
למקם מחדש מטפל קובץ ברמת המערכת, תוך עקיפת buffering של PerlIO.
sysseek מזיז את ה־offset של ה־file-descriptor ברמת הקרנל של FILEHANDLE למיקום בית חדש על־ידי קריאה ל־lseek(2) הבסיסי ישירות. הוא המלווה של sysread ו־syswrite: השלושה יוצרים את משפחת I/O ללא buffering, שמדברת עם הקרנל מבלי לעבור דרך מחסנית המאגרים :perlio או דמוית stdio של Perl. יש להשתמש ב־sysseek על מטפלים שניגשים אליהם עם משפחת ה־sys; יש להשתמש ב־seek על מטפלים שניגשים אליהם עם read, readline, או print.
תקציר#
sysseek FILEHANDLE, POSITION, WHENCE
sysseek $fh, 0, 0 # rewind to start
sysseek $fh, 0, 1 # report current position
sysseek $fh, -1024, 2 # 1024 bytes before EOF
מה מקבלים בחזרה#
ה־offset של הבית המוחלט החדש בהצלחה, או undef בכישלון (כאשר $! מוגדר). מיקום של אפס מוחזר כמחרוזת dualvar "0 but true" כך שהערך מודפס כ־0 בהקשר מספרי אך נשאר אמת בהקשר בוליאני - בדיקה כמו if (sysseek ...) בטוחה ב־offset אפס.
defined(sysseek $fh, $offset, 0)
or die "sysseek to $offset failed: $!";
מכיוון שערך ההחזרה נושא את ה־offset החדש, sysseek משמש כפול גם כפרימיטיב של ״איפה אני״ - הסיבה ש־systell המקובל הוא שורה אחת:
use Fcntl 'SEEK_CUR';
sub systell { sysseek($_[0], 0, SEEK_CUR) }
ערכי WHENCE#
WHENCE הוא מספר שלם עם שלושה ערכים בעלי משמעות. עדיף את הקבועים הסמליים מ־Fcntl - הם גם הופכים את הקריאה ניידת לפלטפורמות שאינן משתמשות ב־0 / 1 / 2:
0/SEEK_SET-POSITIONנמדד מתחילת הקובץ.POSITIONחייב להיות לא־שלילי.1/SEEK_CUR-POSITIONמתווסף למיקום הנוכחי. ערכים שליליים זזים אחורה, ערכים חיוביים קדימה.sysseek $fh, 0, 1מחזיר את ה־offset הנוכחי מבלי להזיז את המצביע.2/SEEK_END-POSITIONמתווסף ל־offset של end-of-file.POSITIONהוא בדרך כלל אפס או שלילי.
use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END);
sysseek $fh, 0, SEEK_SET; # rewind
sysseek $fh, 0, SEEK_END; # move to EOF
sysseek $fh, -$n, SEEK_CUR; # $n bytes back from here
בתים, לא תווים#
sysseek פועל על offsets של בתים, תמיד. אפילו כאשר למטפל יש שכבה מוכוונת־תווים כמו :encoding(UTF-8), ה־offset שהוא מקבל ומחזיר הוא ספירת בתים גולמית. זה תואם לתצוגת הקרנל אך אינו רלוונטי בפועל: sysseek נועד למטפלים שאינם משתמשים בשכבות PerlIO, וערבוב שלו עם שכבת פענוח היא אחת ממלכודות הבלבול המפורטות להלן.
אם הנתונים מוכווני־תווים, יש להשתמש ב־seek וב־tell בצד עם buffering במקום ב־sysseek.
אין לערבב עם I/O עם buffering#
sysseek מדבר ישירות אל ה־file descriptor. read, readline, print, write, seek, tell, ו־eof מדברים אל מאגר ה־PerlIO שיושב לפני ה־descriptor. מיקום המאגר ומיקום הקרנל בלתי־תלויים - sysseek מזיז את מצביע הקרנל אך אינו משטיף או מבטל את המאגר, כך שהקריאה המוצפת הבאה מחזירה בתים מיושנים מלפני ה־sysseek, והכתיבה המוצפת הבאה נוחתת איפה שהמאגר חשב שהוא היה.
הכלל פשוט: לבחור משפחה אחת לכל מטפל.
מצב גלובלי שהוא נוגע בו#
דוגמאות#
קריאת גישה אקראית של רשומה באורך קבוע. הקובץ מחזיק רשומות בנות 128 בתים; לקפוץ ישר לרשומה 42:
use Fcntl 'SEEK_SET';
sysopen my $fh, $path, O_RDONLY or die $!;
sysseek $fh, 42 * 128, SEEK_SET or die "sysseek: $!";
sysread $fh, my $rec, 128 or die "sysread: $!";
לדווח על ה־offset הנוכחי מבלי להזיז את המצביע - המבנה systell:
use Fcntl 'SEEK_CUR';
my $pos = sysseek $fh, 0, SEEK_CUR;
defined $pos or die "sysseek: $!";
print "at byte $pos\n";
לצרף לקובץ שמוחזק פתוח לקריאה־כתיבה, ואז להחזיר מצביע כדי לנגן שוב מההתחלה:
use Fcntl qw(SEEK_SET SEEK_END);
sysseek $fh, 0, SEEK_END or die $!;
syswrite $fh, $record or die $!;
sysseek $fh, 0, SEEK_SET or die $!;
אפס עדיין אמת - ה־dualvar מאפשר לכתוב את הצורה התמציתית מבלי להתייחס בטעות להחזרת מצביע מוצלחת ככישלון:
if (my $pos = sysseek $fh, 0, 0) {
# entered even when $pos stringifies to "0"
printf "rewound; pos=%d\n", $pos;
}
else {
die "sysseek failed: $!";
}
לבצע seek מעבר ל־end-of-file כדי ליצור חור דליל, ואז לכתוב את סמן הזנב:
use Fcntl 'SEEK_SET';
sysseek $fh, 1_000_000, SEEK_SET or die $!;
syswrite $fh, "END" or die $!;
מקרי קצה#
מטפלים שלא ניתן לבצע seek עליהם: pipes, סוקטים,
TTYs, ורוב ההתקנים המיוחדים נכשלים כאשר$!מוגדר ל־ESPIPE(Illegal seek). ערך ההחזרה הואundef; תמיד יש לבדוק עםdefined.אין לערבב עם I/O עם buffering על אותו מטפל.
sysseekעוקף את מאגר ה־PerlIO;read,readline,print,write,seek,tell, ו־eofמשתמשים בו. שילוב שתי המשפחות מייצר תוצאות שגויות בשקט. יש לבחור משפחה אחת לכל מטפל ולהישאר איתה.אפס ממוחרזת כ־
"0 but true". בדיקות בוליאניות עובדות ב־offset אפס -if (sysseek ...)נשאר אמת - אך השוואות מחרוזת מול ה־literal"0"לא:my $pos = sysseek $fh, 0, 0; $pos == 0 or die; # true $pos eq "0" and die "nope"; # false - the string is "0 but true"
יש להשתמש ב־
definedלזיהוי הצלחה/כישלון ובהשוואה מספרית עבור ערך ה־offset.שכבות קידוד:
sysseekעל מטפל עם שכבת:encoding(...)היא כמעט תמיד טעות - ה־offset של הבית יכול לנחות באמצע נקודת קוד, ומשפחת ה־sys נועדה לבתים גולמיים בכל מקרה. אם הנתונים דורשים פענוח, יש להשתמש ב־seekעם המשפחה עם buffering.offsets שליליים עם
SEEK_SET:POSITIONחייב להיות לא־שלילי כאשרWHENCEהוא0. ערך שלילי נכשל עםEINVAL.קבצים גדולים מאוד:
sysseekמחזיר את ה־offset המלא של 64 ביט של הקרנל; מיקומים מעבר ל־2 GiB עוברים round-trip נכון דרך קריאותsysseekעוקבות בכל הבניות 64 ביט נתמכות.מטפל קובץ סגור: מחזיר
undefומגדיר את$!; תחתuse warningsנפלטת אזהרתsysseek() on closed filehandle.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
seek- מקבילה עם buffering; יש להשתמש בה על מטפלים שניגשים אליהם דרךread,readline, אוprintsysread- הקריאה ללא buffering המשויכת ל־sysseek; שניהם מדלגים על PerlIO ומדברים ישירות עם ה־file descriptorsyswrite- הכתיבה ללא buffering; החבר השלישי של המשפחה ששייך לאותו מטפל כמוsysseeksysopen- פותח מטפל ברמת ה־descriptor, הדרך הרגילה לקבל מטפל שתוביל אותו אחר כך עם משפחת ה־systell- שאילתת offset נוכחי בצד עם buffering; יש לשייך עםseek, לא עםsysseekFcntl- מקור של הקבועיםSEEK_SET,SEEK_CUR,SEEK_END