קלט/פלט#

ה-filehandle הוא הגבול בין עולם הטקסט של תוכניתך לבין העולם החיצוני של בתים. לעשות Unicode נכון פירושו להקים את הגבול הזה כראוי בעת open (או באמצעות binmode), כך ש-print ו-readline רגילים כבר מדברים בקידוד הנכון.

שכבות PerlIO#

כל filehandle נושא מחסנית של שכבות - חלקי מנגנון קטנים שמשנים בתים כשהם חוצים את הגבול. הרלוונטיות ל-Unicode הן:

  • :raw - ללא טרנספורמציה. בתים נכנסים, בתים יוצאים.

  • :utf8 - בדוק שהבתים הנקראים מרכיבים UTF-8 תקין וסמן את המחרוזת כטקסט. בטח במשתמש שלא ייצר UTF-8 בלתי-תקין בכתיבות.

  • :encoding(ENCODING) - פענח בקריאה, קודד בכתיבה, עבור כל קידוד בעל שם. דוחה קלט פגום כברירת מחדל.

  • :crlf - תרגם "\r\n" ל-"\n" בקריאה, "\n" ל-"\r\n" בכתיבה. ב-Linux זה אינו פעיל; ב-Windows זוהי ברירת המחדל ל-handles של טקסט.

:encoding(UTF-8) הוא מה שאתה כמעט תמיד רוצה. העדף אותו על פני השכבה החשופה :utf8 - הצורה :encoding(...) מאמתת, :utf8 לא.

פתיחת קובץ עם קידוד#

הצורה בעלת שלושת הארגומנטים של open מקבלת מצב שיכול לכלול שכבות:

open my $in,  "<:encoding(UTF-8)", "input.txt"  or die "open: $!";
open my $out, ">:encoding(UTF-8)", "output.txt" or die "open: $!";

while (my $line = <$in>) {
    chomp $line;                        # characters, not bytes
    print $out uc $line, "\n";          # encoded on the way out
}

כל readline מ-$in מחזיר מחרוזת טקסט. כל print ל-$out מקודד ל-UTF-8 על הקו. גוף הלולאה רואה רק תווים - בלי decode, בלי encode, בלי מניית בתים בטעות.

התאמה לאחור עם binmode#

אם ה-filehandle כבר פתוח - לדוגמה הזרמים הסטנדרטיים, או handle שנמסר לך על ידי מודול אחר - צרף את השכבה באמצעות binmode:

binmode STDIN,  ":encoding(UTF-8)";
binmode STDOUT, ":encoding(UTF-8)";
binmode STDERR, ":encoding(UTF-8)";

עשה זאת פעם אחת, בראש התוכנית, לפני כל I/O על אותם handles. לאחר מכן הזרמים הסטנדרטיים מתנהגים כמו קובץ שנפתח עם שכבת קידוד.

הפרגמה use open#

כתיבת אותה שכבת :encoding(UTF-8) על כל open היא רועשת. הפרגמה open מגדירה את שכבות ברירת המחדל לכל הפתיחות הבאות בתחום העוטף:

use open ":std", ":encoding(UTF-8)";

התג :std גם משכבת מחדש את STDIN, STDOUT ו-STDERR במקום, כך שאתה מקבל את האפקט של שלוש קריאות binmode וברירת מחדל לכל open מאוחר יותר בשורה אחת.

זוהי שורת-ה-one-liner בראש סקריפט מודרני מודע-Unicode:

use utf8;
use open ":std", ":encoding(UTF-8)";
  • use utf8 - ליטרלים בקובץ מקור זה הם UTF-8.

  • use open ":std", ":encoding(UTF-8)" - stdin/stdout/stderr וכל פתיחה מאוחרת יותר מוגדרים כברירת מחדל ל-UTF-8 על הקו.

תוכל גם לדחוף I/O ל-UTF-8 מחוץ לתוכנית באמצעות משתנה הסביבה PERL_UNICODE. האותיות I, O, E בוחרות את stdin, stdout ו-stderr בנפרד, S בוחר את שלושתם בבת אחת באופן שבו הפרגמה use open ":std" עושה, ו-A מפענח את @ARGV, כך ש-PERL_UNICODE=SA הוא בקירוב המקבילה בשורת הפקודה של ההקדמה בת שתי השורות לעיל. כדי לפענח ארגומנטים בתוך התוכנית במקום זאת, כתוב @ARGV = map { decode('UTF-8', $_, 1) } @ARGV.

מתג שורת הפקודה התואם -C (-CSA) עדיין אינו מזוהה על ידי pperl, וזו חריגה מפרל הסטנדרטית; השתמש במשתנה PERL_UNICODE או בפרגמה use open עד שהוא ימומש.

I/O בינארי לצד I/O טקסטואלי#

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

open my $img, "<:raw", "photo.jpg" or die "open: $!";

בתוך תוכנית אחת, חלק מה-handles יכולים להיות טקסט (:encoding(UTF-8)) ואחרים בינאריים (:raw). שמור על התוויות ברורות: handle בינארי נותן לך מחרוזות בתים, handle טקסט נותן לך מחרוזות טקסט, ו-$! הוא אותו דבר בשני המקרים.

קלט אינטראקטיבי#

מסוף אינו אומר לך באיזה קידוד הוא מקליד. ב-Linux מודרני זה כמעט תמיד UTF-8; ב-Windows דף-הקוד של ברירת המחדל בקונסולה הוא עדיין לרוב CP1252 או CP437. ההנחה הרגילה לסקריפטים חוצי-פלטפורמה היא UTF-8, עם binmode המיושם בהפעלה:

binmode STDIN, ":encoding(UTF-8)";

אם תוכניתך חייבת לתמוך במסופים ישנים, פענח בהתבסס על משתנה הסביבה LANG או LC_CTYPE, או קבל דגל --encoding. אין זיהוי-אוטומטי נייד.

בדיקת שכבות#

כדי לראות אילו שכבות נמצאות על handle, השתמש ב-PerlIO::get_layers:

use PerlIO;
my @layers = PerlIO::get_layers(\*STDOUT);
print "@layers\n";                       # e.g. unix perlio encoding(utf-8-strict)

שימושי כשתוכנית מייצרת בחשאי פלט מקודד-כפול ואתה רוצה לגלות לאיזה handle יש את שכבת ההפתעה עליו.