מתכונים#
הפרקים הרעיוניים מכסים את המשמעת; עמוד זה הוא נספח העיון המהיר. כל מתכון הוא תשובה קצרה ועצמאית למשימת Unicode נפוצה - מהסוג שספר הבישול הקלאסי של פרל ל-Unicode פונה למודול CPAN כדי לבצע, אך ש-pperl מטפלת בו ישירות באמצעות הפונקציות המובנות שלה ומנוע ה-regex שלה.
כל דוגמה כאן מניחה שהמחרוזות שלך הן כבר טקסט - מפוענחות בדרך פנימה, כפי שמתאר הפרק Strings and encodings. הפעולות שלהלן כולן מדברות בתווים, ולכן מחרוזת בינארית שתוזן להן תמנה בתים ותפתיע אותך.
מיון והשוואה ללא רגישות לאותיות גדולות/קטנות עם fc#
הפונקציה המובנית fc מחזירה את ה-foldcase של מחרוזת - אותה קיפול אותיות (case folding) של Unicode שבו משתמש משנה התבנית /i. שתי מחרוזות שוות תחת קיפול אותיות בדיוק כאשר fc מחזיר את אותה התוצאה לשתיהן, מה שהופך את fc לכלי הנכון למיון והשוואה ללא רגישות לאותיות גדולות/קטנות.
use v5.36; # or: use feature 'fc';
use utf8;
# sort case-insensitively
my @sorted = sort { fc($a) cmp fc($b) } @list;
# both are true:
fc("tschüß") eq fc("TSCHÜSS");
fc("Σίσυφος") eq fc("ΣΊΣΥΦΟΣ");
קיפול מטפל במקרים שבהם lc הפשוט אינו יכול: ה-s החדה הגרמנית מתקפלת ל-ss, וסיגמא סופית יוונית ς מתקפלת יחד עם σ באמצע המילה. פנה אל fc במקום אל lc בכל פעם שההשוואה אמורה להתעלם מאותיות גדולות/קטנות במובן המלא של Unicode.
זהו קיפול אותיות בלבד. הוא אינו מתעלם מסימני הטעמה, והוא אינו כופה את הסדר האלפביתי של locale - לשם כך תזדקק לספריית collation כגון Unicode::Collate, שעדיין אינה שמישה ב-pperl (ראה את סוף עמוד זה).
נרמול מעברי שורה עם \R#
\R מתאים למעבר שורה בודד של Unicode: הגרפמה CRLF בת שני התווים, או כל אחד מתווי הרווח-הלבן האנכי (LF, CR, form feed, tab אנכי, next line, מפריד שורה, מפריד פסקה). זוהי הדרך הניידת לטפל בקבצי טקסט שמגיעים ממערכות הפעלה שונות.
my $text = "line one\r\nline two\rline three\n";
$text =~ s/\R/\n/g; # normalise every linebreak to \n
אחרי ההחלפה, $text משתמש ב-\n בודד בכל מקום ללא קשר לאופן שבו הקלט קידד את סופי השורות שלו. השתמש ב-\R במקום בחלופה מפורשת \r?\n: הוא מכסה את המקרים שהתבנית הכתובה ביד שוכחת.
התאמת אשכול גרפמות עם \X#
תו הנראה למשתמש - אות בסיס יחד עם כל סימני שילוב - הוא אשכול גרפמות. מטא-התו . מתאים לנקודת קוד אחת, שעשויה להיות רק חלק מאשכול כזה; \X מתאים לאשכול גרפמות שלם אחד.
use utf8;
# "é" written as e + COMBINING ACUTE ACCENT is two code points,
# but one grapheme.
my $str = "cafe\x{301}"; # c a f e + combining acute
$str =~ /^.{5}$/; # matches - five code points
$str =~ /^\X{4}$/; # matches - four graphemes
# find a vowel plus any combining diacritics
$str =~ / (?=[aeiou]) \X /xi;
\X הוא היחידה הנכונה בכל פעם ש“תו“ פירושו ”מה שהקורא רואה“ ולא ”נקודת קוד“. המתכונים שלהלן נבנים עליו.
N הגרפמות הראשונות, היפוך, ואורך לפי גרפמה#
שלוש המשימות הללו מצטמצמות כולן ליישום של \X במקום אינדוקס לפי נקודת קוד. הן עובדות נכון ללא קשר לצורת הנרמול שבה נמצאת המחרוזת.
תפוס את חמש הגרפמות הראשונות:
my ($first_five) = $str =~ /^ ( \X{5} ) /x;
הפוך מחרוזת לפי גרפמה. היפוך לפי נקודת קוד היה מנתק סימן שילוב מאות הבסיס שלו ומשחית את הטקסט; איסוף הגרפמות תחילה שומר על כל אשכול שלם:
$str = join "", reverse $str =~ /\X/g;
ספור את אורך המחרוזת בגרפמות. המילה brûlée היא שש גרפמות אך עשויה להיות עד שמונה נקודות קוד, ולכן length ומניין גרפמות יכולים להיבדל:
my $count = 0;
$count++ while $str =~ /\X/g;
כוונון עדין של אזהרות UTF-8#
פרל מפצלת את אזהרות ה-UTF-8 לשלוש תת-מחלקות, כך שתוכל להשתיק סוג אחד של אזהרת Unicode-פגום מבלי להשתיק את האחרות. pperl מזהה את כל שלושת שמות הקטגוריות:
no warnings "nonchar"; # the forbidden non-characters
no warnings "surrogate"; # UTF-16 surrogate code points
no warnings "non_unicode"; # code points above 0x10_FFFF
השבת רק את תת-המחלקה שיש לך סיבה מכוונת להתיר - לדוגמה no warnings "non_unicode" בקוד שמטפל בנקודות קוד מעבר לטווח Unicode בכוונה - והשאר את השאר בתוקף כך שתקלות קידוד אמיתיות עדיין יצופו.
פענוח @ARGV#
ארגומנטים של שורת הפקודה מגיעים כבתים. אם משתמש מעביר ארגומנט שאינו ASCII, עליך לפענח אותו לפני שתתייחס אליו כטקסט, בדיוק כפי שהיית מפענח כל קלט אחר. הצורה הניידת מבצעת map של decode על @ARGV:
use Encode qw(decode);
@ARGV = map { decode('UTF-8', $_, 1) } @ARGV;
אחרי שורה זו, כל איבר של @ARGV הוא מחרוזת טקסט ומתנהג תחת length, regex ופעולות אותיות בתווים ולא בבתים. הארגומנט השלישי 1 (בדיקת ה-Encode::FB_CROAK) הופך ארגומנט פגום לשגיאה קטלנית ולא להחלפה שקטה, שבדרך כלל זה מה שאתה רוצה עבור קלט שלא ניתן לבקשו מחדש.
תכונה מותאמת אישית לפי טווח נקודות-קוד#
A \p{...} property can name a subroutine you define. When the regex engine meets an unknown property \p{In_Foo}, it calls In_Foo, which returns a list of code-point ranges (one START\tEND pair per line, in hexadecimal); the property then matches any character in those ranges.
sub In_Greek { return "0370\t03FF\n0780\t07BF\n" }
"\x{3b1}" =~ /\p{In_Greek}/; # matches - alpha is in 0370..03FF
זוהי הדרך הניידת להגדיר מחלקת תווים אד-הוק פעם אחת ולעשות בה שימוש חוזר לפי שם על פני תבניות, מבלי לכתוב את הטווח בכל regex.
מודולים בהמתנה ב-pperl#
קומץ מתכונים בספר הבישול הקלאסי של פרל ל-Unicode פונים למודולי העזר של Unicode שפרל מספקת כהרחבות מהודרות: Unicode::Normalize (נרמול NFC, NFD, NFKC, NFKD), Unicode::Collate ו-Unicode::Collate::Locale (מיון אלפביתי ורגיש-locale, השוואה בלתי-רגישה לסימני הטעמה), Unicode::GCString (substr מודע-גרפמות ורוחב עמודת-הדפסה), Unicode::UCD (חיפוש תכנותי של קטגוריית תו וערך מספרי), ו-Unicode::LineBreak (שבירת שורות לפי כללי Unicode).
מודולים אלה עדיין אינם זמינים ב-pperl: טעינת אחד מהם כיום נכשלת עם dynamic loading not available in this perl. עד שיהיו, משפחת ה-regex \X לעיל מכסה אורך מודע-גרפמות, היפוך וחילוץ בכמות קבועה (אם כי לא רוחב עמודת-הדפסה), ואין כאן תחליף לנרמול או ל-collation של locale. תווים בעלי שם ותכונות מוגדרות-משתמש, לעומת זאת, עובדים כיום, כפי שמראים המתכונים לעיל.