# יוניקוד המחרוזות של Perl הן מחרוזות יוניקוד. כל נקודת קוד מ־`\x{0000}` עד `\x{10FFFF}` היא תו תקף, ללא קשר לקידוד הבייטים שמתחת. ביטויים רגולריים פועלים על תווים, לא על בייטים — לכן `\w`, `\d`, `\s`, `.`, ו־`\X` כולם מבינים את עולם היוניקוד. פרק זה מכסה כיצד לכתוב תווי יוניקוד בתבנית, כיצד להתאים לפי תכונת יוניקוד, וכיצד *מתאמי ערכת התווים* `/a`, `/u`, `/l`, `/d` מכווננים את התנהגות ברירת המחדל של מחלקות תווים. הוא נחתם ב *ריצות כתב*, המנגנון לדחיית מחרוזות שמערבבות כתבים כאשר אסור להן. ## כתיבת יוניקוד בתבנית ארבע דרכים לציין נקודת קוד ספציפית: | צורה | דוגמה | הערות | |------------|--------------------------------|-----------------------| | `\xHH` | `\x41` (A) | שתי ספרות הקסה, 0–255 | | `\x{…}` | `\x{263a}` (☺) | כל נקודת קוד | | `\o{…}` | `\o{101}` (A) | אוקטלי | | `\N{name}` | `\N{GREEK SMALL LETTER SIGMA}` | שם תו יוניקוד | ```perl /\x{263a}/; # WHITE SMILING FACE /\N{GREEK SMALL LETTER SIGMA}/; # σ /\N{U+03C3}/; # same codepoint via U+ form ``` רווחים לבנים וקווים תחתונים בתוך `\N{…}` ו־`\p{…}` מתעלמים מהם, כך שניתן לכתוב `\N{GREEK SMALL LETTER SIGMA}` או `\N{greek_small_letter_sigma}`. בתוך מחלקת תווים, אותם רצפי בריחה עובדים: ```perl /[\x{0370}-\x{03ff}]/; # any Greek block character /[\N{SECTION SIGN}\N{PILCROW SIGN}]/; ``` `\N{NAME}` (תו בשם) *אינו* זהה ל־`\N` (כל תו פרט ל־`\n` — ראו את פרק [character classes](character-classes.md)). נוכחות או היעדר הסוגריים המסולסלים הם הבוחרים. ## התאמה לפי תכונה: `\p{…}` ו־`\P{…}` יוניקוד מסווג כל נקודת קוד לפי עשרות תכונות. השימושיות ביותר בתבניות יומיומיות: | תכונה | מתאים | |-------------------|-----------------------------------| | `\p{L}` | כל אות | | `\p{Ll}` | אות קטנה | | `\p{Lu}` | אות גדולה | | `\p{Lt}` | אות בכתיב כותרת | | `\p{N}` | כל תו מספרי | | `\p{Nd}` | ספרה עשרונית | | `\p{P}` | פיסוק | | `\p{M}` | סימן (תו משולב) | | `\p{S}` | סמל | | `\p{Z}` | מפריד (כולל רווחים) | | `\p{C}` | בקרה, פורמט, לא משויך, שימוש פרטי | | `\p{ASCII}` | נקודת קוד 0–127 | | `\p{White_Space}` | כל רווח לבן של יוניקוד | `\P{X}` היא השלילה של `\p{X}` — מתאימה כל נקודת קוד שאינה בתכונה X. תכונות קצרות של אות בודדת מוותרות על הסוגריים המסולסלים: ```perl /\pL/; # letter — same as \p{L} /\pN/; # number /\pM/; # mark ``` ### תכונות מורכבות לחלק מהתכונות יש מספר ערכים. הצורה המורכבת `\p{Property=Value}` (או `\p{Property:Value}`) מציינת אותם במפורש: ```perl /\p{General_Category=Uppercase_Letter}/; # same as \p{Lu} /\p{Block=Greek_and_Coptic}/; /\p{Numeric_Type=Decimal}/; /\p{Bidi_Class=R}/; # right-to-left characters ``` אותיות גדולות/קטנות, רווחים וקווים תחתונים מתעלמים מהם בשם התכונה ובערך, לכן `\p{gc=lu}` זהה ל־`\p{General_Category=Uppercase_Letter}`. לרשימה המלאה של תכונות וערכים, ראו [`perluniprops`](https://perldoc.perl.org/perluniprops) — הרשימה הזו מיוצרת מנתוני יוניקוד ואינה משוכפלת כאן. ## התאמה לפי כתב כתבים (מערכות כתיבה) הם גם תכונות: ```perl /\p{Latin}/; /\p{Greek}/; /\p{Cyrillic}/; /\p{Arabic}/; /\p{Han}/; # Chinese, Japanese kanji, Korean hanja /\p{Hiragana}/; /\p{Katakana}/; /\p{Hangul}/; /\p{Script=Greek}/; # compound form, same meaning ``` לצורכי סיווג לשוני, יש להעדיף `\p{Script_Extensions=…}` — הוא מטפל בנקודות קוד המשמשות כתבים מרובים בצורה הגיונית יותר מ־`\p{Script=…}`. הקיצור `\p{Greek}` של Perl למעשה משתמש ב־`Script_Extensions` מתחת למכסה. ## אשכולות גרפמות: `\X` *אשכול גרפמות* הוא מה שמשתמשים תופסים כתו יחיד. `å` דנית יכולה להיות נקודת קוד אחת (`U+00E5`) או שתיים (`A` ואחריה טבעת משלבת `U+030A`). שתיהן נראות זהות על המסך. `\X` מתאים לכל אחת מהן — תו אטומי אחד הנראה למשתמש. ```perl my $s = "A\x{030A}"; # A + combining ring length($s); # 2 — code-point count $s =~ /./; # matches just the 'A' $s =~ /\X/; # matches the whole cluster ``` יש להשתמש ב־`\X` כאשר מבצעים איטרציה הנראית על פני טקסט הפונה למשתמש: תנועת סמן, ספירת עמודות, חיתוך, בכל מקום שבו ״תווים״ פירושו ״מה שהמשתמש רואה״. ## מחלקות תווים מורחבות בסוגריים — `(?[…])` הצורה הסטנדרטית `[…]` לוקחת איחודים של תווים; `(?[…])` מוסיף אופרטורים של אלגברת קבוצות שמשלבים תכונות יוניקוד: ```perl # Letters that are Greek: /(?[ \p{Letter} & \p{Greek} ])/; # Letters that are not Latin: /(?[ \p{Letter} - \p{Latin} ])/; # Hex digits without 'a' through 'f': /(?[ [0-9A-Fa-f] - [a-f] ])/; ``` אופרטורים: `+` (או `|`) לאיחוד, `&` לחיתוך, `-` להפרש, `^` להפרש סימטרי, `!` למשלים. המבנה הוא בעצמו מחלקת תווים — הוא מתאים תו אחד — ומקבל כמת רגיל. פרק [character classes](character-classes.md) מכסה אותו במלואו. ## מתאמי ערכת התווים הדגלים `/a`, `/u`, `/l`, `/d` שולטים באופן שבו המחלקות המקוצרות (`\d`, `\w`, `\s`) וקיפול האותיות מתנהגים. הם אורתוגונליים ל־`/i`, `/m`, `/s`, `/x`. לכל היותר אחד יכול להיות בתוקף בכל פעם; `/aa` הוא חידוד של `/a`. ### `/u` — סמנטיקת יוניקוד `\d`, `\w`, `\s`, `\b`, קיפול אותיות, ו־`[[:alpha:]]` משתמשים בהגדרות יוניקוד מלאות. זוהי ברירת המחדל כאשר `use v5.12` או מאוחר יותר בתוקף. ```perl "item\x{0660}" =~ /\w+\d/u; # matches — U+0660 is an Arabic-Indic zero "naïve" =~ /\w+/u; # matches the whole word ``` קוד מודרני צריך להשתמש ב־`/u`. `use v5.12+` בוחר בו אוטומטית. ### `/a` — מחלקות ASCII בלבד מגביל את `\d`, `\w`, `\s`, `[[:…:]]` לטווח ASCII. שימושי כאשר *רוצים* טקסט בסגנון אנגלי ולא יותר: ```perl "item\x{0660}" =~ /\w+\d/a; # does not match — \d is [0-9] under /a "item0" =~ /\w+\d/a; # matches ``` ### `/aa` — ASCII קפדני `/aa` (כפול) מונע בנוסף התאמות בלי רגישות לאותיות גדולות/קטנות שחוצות את הגבול ASCII/לא־ASCII. תחת `/ia` רגיל, `K` יכול היה להתאים `\x{212A}` (KELVIN SIGN) מכיוון שסימן קלווין מתקפל לאות `k`. תחת `/iaa` הוא לא: ```perl "K" =~ /k/i; # matches "\x{212A}" =~ /k/i; # matches — Kelvin sign casefolds to 'k' "\x{212A}" =~ /k/iaa; # does NOT match — ASCII-only i ``` `/aa` היא הצורה הקפדנית כאשר הקלט הוא באמת ASCII בלבד ורוצים שהמנוע יסרב להתאמות יוניקוד גם תחת `/i`. ### `/l` — סמנטיקת locale `\d`, `\w`, `\s`, קיפול אותיות, ו־`[[:alpha:]]` עוקבים אחר ה־locale POSIX הנוכחי. הגיוני רק לאחר `use locale`. לעיתים רחוקות זה מה שרוצים — התנהגות locale ספציפית למימוש וקשה יותר לשיקול מ־`/a` או `/u`. מסופק עבור קוד מורשת שזקוק באמת להתאמה תלוית־locale. ### `/d` — סמנטיקת מצב כפול תחת `/d`, `\d`, `\w`, `\s` תלויים בכך שמחרוזת המטרה מסומנת כ־UTF-8 פנימית. זהו המקור לרוב הבאגים של ביטויים רגולריים ביוניקוד מסוג ״עובד אצלי במחשב״. **יש להימנע מ־`/d` בקוד חדש.** ברירת המחדל תחת `use v5.12` היא `/u`. אם יורשים קוד שבו `/d` משנה, אופן הכישלון הוא: אותה מחרוזת קלט מפיקה תוצאות התאמה שונות בהתאם לכך שפעולה כלשהי במעלה הזרם סימנה את המחרוזת כ־UTF-8. התיקון הוא `use feature 'unicode_strings'` (או `use v5.12+`), שהופך את הכללים לבלתי תלויים בדגל ה־UTF-8. ### איזה מתאם בתוקף? סדר הפתרון: 1. מתאם מפורש על הביטוי הרגולרי (`/a`, `/u`, `/l`, `/d`). 2. `use re '/u'` (או אחר) בתחום הלקסיקלי. 3. `use locale` בוחר `/l`. 4. `use feature 'unicode_strings'` (ו־`use v5.12+`) בוחר `/u`. 5. אחרת, `/d` (הנפילה למצב הכפול; יש להימנע). תוכניות צריכות להשתמש ב־`use v5.12;` (או מאוחר יותר) בראש כל קובץ; זה מספק `/u`, וזה מה שרוצים. ## אינטראקציות פרגמה ```perl use v5.12; # implicit feature 'unicode_strings' use feature 'unicode_strings'; # explicit use re '/u'; # set default modifier for this scope ``` `use feature 'unicode_strings'` (כלול ב־`use v5.12`) הופך את כל המחרוזות ליוניקוד לצורכי מחלקות תווים, ללא קשר לכך שהן מסומנות כ־UTF-8 פנימית. יש להפעיל אותה בראש כל קובץ. ## קיפול אותיות תחת `/iu` (ברירת המחדל), התאמה בלי רגישות לאותיות גדולות/קטנות משתמשת בטבלאות קיפול האותיות המלאות של יוניקוד: ```perl "Grüße" =~ /grüsse/iu; # matches — ß folds to 'ss' "Σίγμα" =~ /σίγμα/iu; # matches — upper Σ ↔ lower σ "İstanbul" =~ /istanbul/iu; # matches — dotted I folds ``` תחת `/ia`, מקפל רק אותיות ASCII. תחת `/iaa`, בנוסף לכלל קלווין/K שלמעלה. הקיפול מתרחש בזמן הידור — pperl אינו הופך את מחרוזת הקלט לאותיות קטנות בזמן ההתאמה. ## וריאנטים של גבול מילה `\b{…}` מעבר ל־`\b` רגיל, Perl מציעה וריאנטים סמנטיים המטפלים בטקסט מהעולם האמיתי טוב יותר: | גבול | משמעות | |-----------|-----------------------------------------------------------| | `\b{wb}` | גבול מילה של יוניקוד — מטפל ב־`don't`, `state-of-the-art` | | `\b{sb}` | גבול משפט | | `\b{lb}` | שבירת שורה (מתאים לעטיפת שורות) | | `\b{gcb}` | גבול אשכול גרפמות (אותה השפעה כמו `\X`) | ```perl "don't" =~ /.+?\b{wb}/x; # matches whole word — apostrophe is inside "don't" =~ /.+?\b/x; # stops at the apostrophe — plain \b splits ``` לעיבוד שפה טבעית, `\b{wb}` ו־`\b{sb}` הם כמעט תמיד מה שרוצים. `\b` רגיל מיועד להקשרים של מזהי ASCII. פרק [anchors and assertions](anchors-and-assertions.md) כולל את הדיון הרחב יותר על גבולות; פרק זה הוא הבית עבור הוריאנטים מודעי יוניקוד מכיוון שההגדרות שלהם מונעות יוניקוד. ## ריצות כתב למערכות כתיבה שונות יש תווים דומים ויזואלית. ה־`a` הלטינית, ה־`а` הקירילית (U+0430), וה־`α` היוונית (U+03B1) נראות בלתי ניתנות להבחנה. בשימוש משולב — בדרך כלל ב־URL או במזהה — הן מאפשרות *התקפת הומוגרף*: > paypal.com יכול להיות כולו לטיני (לגיטימי). יכול להיות `а` קירילית משובצת בתוך טקסט לטיני אחרת (התקפת פישינג שמצביעה לדומיין אחר). ה־URL של הדפדפן מוצג זהה; הבייטים לא. *ריצת כתב* היא רצף של תווים שכולם מאותו כתב יוניקוד (לפי `Script_Extensions` ו־Unicode UTS 39). רוב המילים הלגיטימיות הן ריצות כתב; מחרוזות מעורבות־כתב נדירות מחוץ להקשרים רב־לשוניים ספציפיים וכמעט תמיד חשודות. Perl מספקת ארבעה מבנים כדי לדרוש שתת־תבנית מותאמת תהיה ריצת כתב: | צורה | משמעות | |-------------------------------------------|-------------------------------------------------| | `(*script_run:PAT)` / `(*sr:PAT)` | התאם את `PAT`, ואז בדוק שזו ריצת כתב | | `(*atomic_script_run:PAT)` / `(*asr:PAT)` | אותו דבר, אך אטומי (מהיר יותר, פחות חזרה לאחור) | ```perl # Match a domain label only if it is a script run: $label =~ /(*sr: \w+ )/x or warn "mixed-script label"; # Atomic version: faster on adversarial input. $label =~ /(*asr: \w+ )/x or warn "mixed-script label"; ``` הצורה האטומית היא מה שרוצים ברוב קוד הייצור — היא מונעת את סיכול בדיקת ריצת הכתב על ידי חזרה לאחור קטסטרופלית על קלט זדוני. `(*sr:…)` הוא המקבילה הלא־אטומית (`(*sr:(?>PAT))` זהה ל־`(*asr:PAT)`). ### כללי ריצת כתב רצף הוא ריצת כתב אם ורק אם כל אלה: 1. **אף נקודת קוד ברצף אינה בעלת תכונת `Script_Extension` של `Unknown`.** זה שולל נקודות קוד לשימוש פרטי ונקודות קוד surrogate; אי אפשר להבריח אותן. 2. **כל התווים באים מהכתב Common, מהכתב Inherited, ולכל היותר מכתב אחר אחד.** 3. **כל הספרות העשרוניות באות מאותה קבוצה של עשר.** לכתבים רבים יש ספרות משלהם (ערבית־הודית, דוונגארי, וכו«); מחרוזת המערבת `1` (ASCII) עם `١` (אחת ערבית־הודית) *אינה* ריצת כתב, גם אם תווים אחרים תואמים. שלושה פסאודו־כתבים מקבלים טיפול מיוחד: - **Common** — פיסוק, ספרות ASCII 0–9, סמלים מתמטיים, אמוג’י, ספרות ברוחב מלא. אלה יכולים להופיע בכל ריצת כתב *פרט לכך* שכל הספרות העשרוניות ברצף עדיין חייבות לבוא מאותה קבוצה של עשר. - **Inherited** — סימנים משולבים (טעמים, סימני ניקוד). אלה נצמדים לתו הקודם ויורשים את הכתב שלו. - **Unknown** — נקודות קוד שאינן משויכות וכדומה. מחרוזת חד־תווית של unknown מותרת; מחרוזת ארוכה יותר המכילה אחת כזו אינה. ההקלה עבור Common ו־Inherited היא מה שמאפשר לטקסט מהעולם האמיתי להיות ריצת כתב על אף פיסוק וסימנים משולבים. כלל השוויון הקפדני לספרות הוא מה שמסכל התקפות הומוגרף שמערבבות ספרות מכתבים דומים למראה. ### מתי ריצות כתב עוזרות - **פענוח URL**: לדחות תוויות דומיין שאינן ריצות כתב. - **אימות מזהים**: מזהי שפת תכנות צריכים בדרך כלל להיות ריצות כתב. - **חיטוי קלט טופס**: שמות אמיתיים הם ריצות כתב; שמות מעורבי־כתב הם בדרך כלל התקפות או נתוני בדיקה. ריצות כתב הן תכונה משנת 2018 ואילך. הן מה שמנוע הביטויים הרגולריים של Perl מוסיף לבעיה שאת שאר המנוע לא יכול לפתור ישירות: תכונה של ההתאמה ה *שלמה*, לא של תו יחיד כלשהו. ## בין־מנועי: כיסוי יוניקוד | תכונה | Perl 5.42 | PCRE2 | Emacs (מוגבל) | POSIX | RE2 / Go | |-----------------------------|-------------|---------|-----------------|---------|-------------------------------| | `\p{General_Category}` | כן | כן | חלקי | לא | כן | | `\p{Script}` | כן | כן | חלקי | לא | כן | | `\p{Property=Value}` | כן | כן | חלקי | לא | כן | | אשכול גרפמות `\X` | כן | כן | לא | לא | כן (`(?-X:...)` הוא משהו אחר) | | `\b{wb}`, `\b{sb}` | כן | לא | לא | לא | לא | | ריצות כתב `(*sr:…)` | כן | לא | לא | לא | לא | | ברירת מחדל ליוניקוד `\d/\w` | תחת `/u` | לא | לא | לא | לא (יוניקוד תחת `(?u)`) | Perl מובילה כאן: `\b{wb}` וריצות כתב הן למעשה ייחודיות ל־Perl בקבוצת ההשוואה. RE2 / Go ו־PCRE2 שניהם מוגדרים כברירת מחדל ל־ASCII עבור `\d`, `\w`, `\s`; RE2 מאפשר יוניקוד תחת הדגל `(?u)`. ל־PCRE2 יש תמיכה בתכונות אך חסרים הוריאנטים של גבולות וריצות כתב. ל־Emacs יש תמיכה בתכונות שמשתנה לפי גרסת הבנייה. הטבלה המלאה נמצאת בפרק [cross-engine](cross-engine.md). ## קידוד מול תו מחרוזת Perl היא תמיד רצף של נקודות קוד. הבייטים בדיסק הם דאגה של שכבת הקלט/פלט — יש להשתמש ב־`use utf8` למילוליים בקוד המקור, ב־`:encoding(UTF-8)` על מטפלי קובץ, וב־`decode`/`encode` מ־`Encode` בעת חציית הגבול. תבניות לעולם אינן פועלות על הבייטים המקודדים; הן תמיד פועלות על נקודות קוד. ```perl use utf8; # source code is UTF-8 use feature 'unicode_strings'; use open ':std', ':encoding(UTF-8)'; # STDIN/STDOUT/STDERR while (my $line = <>) { $line =~ /\p{Lu}/ and print "has uppercase letter\n"; } ``` לטפל בקלט/פלט נכון פעם אחת, ותבניות פשוט עובדות על תווים לכל יתר התוכנית. ## ראו גם - פרק [character classes](character-classes.md) — יסודות מחלקות שאינן יוניקוד, `(?[…])`. - פרק [anchors and assertions](anchors-and-assertions.md) — `\b`, `\B`, ובנוסף משפחת `\b{…}` בתפקידם כגבולות. - פרק [modifiers](modifiers.md) — `/i`, `/x`, `/m`, `/s`, וכיצד הם משתלבים עם מתאמי ערכת התווים. - פרק [cross-engine](cross-engine.md) — כיסוי יוניקוד על פני מנועי ביטויים רגולריים. - [`quotemeta`](../../p5/core/perlfunc/quotemeta.md) — מילוט תווים מטא מודע ליוניקוד.