אנו מציגים לתשומת לבך תרגום של מדריך קצר לביטויים רגולריים בג'אווה, שנכתב על ידי ג'ף פריזן עבור אתר
javaworld . כדי להקל על הקריאה, חילקנו את המאמר למספר חלקים.
ביטויים רגולריים ב-Java, חלק 1
מיזוג טווחים מרובים |
אתה יכול למזג טווחים מרובים למחלקת תו טווח אחת על ידי הצבתם זה לצד זה. לדוגמה, המחלקה [a-zA-Z] מתאימה לכל התווים האלפביתיים הלטינים באותיות קטנות או רישיות. |
מיזוג טווחים מרובים
אתה יכול למזג טווחים מרובים למחלקת תו טווח אחת על ידי הצבתם זה לצד זה. לדוגמה, המחלקה
[a-zA-Z]
מתאימה לכל התווים האלפביתיים הלטינים באותיות קטנות או רישיות.
שילוב כיתות אופי
איחוד מחלקות תווים מורכב מכמה מחלקות תווים מקוננות ומתאים לכל התווים באיחוד שנוצר. לדוגמה, הכיתה
[a-d[m-p]]
מתאימה את התווים מ-
a
אל
d
ומ-
m
אל
p
. שקול את הדוגמה הבאה:
java RegexDemo [ab[c-e]] abcdef
דוגמה זו תמצא את התווים
a
,
b
,
c
,
d
ו-
e
שעבורם יש התאמות ב-
abcdef
:
regex = [ab[c-e]]
input = abcdef
Found [a] starting at 0 and ending at 0
Found [b] starting at 1 and ending at 1
Found [c] starting at 2 and ending at 2
Found [d] starting at 3 and ending at 3
Found [e] starting at 4 and ending at 4
צומת כיתת דמות
ההצטלבות של מחלקות תווים מורכבת מתווים משותפים לכל המחלקות המקוננות ומתאים רק לתווים הנפוצים. לדוגמה, הכיתה
[a-z&&[d-f]]
תואמת את התווים
d
,
e
ו
f
. שקול את הדוגמה הבאה:
java RegexDemo "[aeiouy&&[y]]" party
שים לב שבמערכת ההפעלה Windows שלי, יש צורך במירכאות כפולות מכיוון שמעטפת הפקודה מתייחסת אליהם
&
כמפריד פקודה. דוגמה זו תמצא רק את הדמות
y
שיש לה התאמה ב
party
:
regex = [aeiouy&&[y]]
input = party
Found [y] starting at 4 and ending at 4
הפחתת כיתות תווים
הפחתת מחלקות תווים מורכבת מכל התווים מלבד אלה הכלולים במחלקות תווים מקוננות, ומתאימה רק לאותם התווים הנותרים. לדוגמה, המחלקה
[a-z&&[^m-p]]
מתאימה את התווים מ
a
אל
l
ומאל : דוגמה זו תמצא את התווים
q
ועבורם יש התאמות ב :
z
java RegexDemo "[a-f&&[^a-c]&&[^e]]" abcdefg
d
f
abcdefg
regex = [a-f&&[^a-c]&&[^e]]
input = abcdefg
Found [d] starting at 3 and ending at 3
Found [f] starting at 5 and ending at 5
כיתות תווים מוגדרות מראש
מחלקות תווים מסוימות מופיעות בתדירות מספיקה
בביטויים רגולריים כדי להצדיק את השימוש בתווים קצרים. המחלקה
Pattern
מציעה מחלקות תווים מוגדרות מראש כקיצורים כאלה. אתה יכול להשתמש בהם כדי לפשט את הביטויים הרגולריים שלך ולמזער שגיאות תחביר. ישנן מספר קטגוריות של מחלקות תווים מוגדרות מראש:
java.lang.Character
מאפיינים סטנדרטיים, POSIX ו-Unicode כגון סקריפט, בלוק, קטגוריה ובינארי. הרשימה הבאה מציגה רק את הקטגוריה של כיתות סטנדרטיות:
\d
: מספר. שווה ערך [0-9]
.
\D
: תו לא מספרי. שווה ערך [^0-9]
.
\s
: תו רווח לבן. שווה ערך [ \t\n\x0B\f\r]
.
\S
: לא תו רווח לבן. שווה ערך [^\s]
.
\w
: סמל יוצר מילים. שווה ערך [a-zA-Z_0-9]
.
\W
: לא דמות יוצרות מילים. שווה ערך [^\w]
.
הדוגמה הבאה משתמשת במחלקת תווים מוגדרת מראש
\w
כדי לתאר את כל תווי המילים בטקסט הקלט:
java RegexDemo \w "aZ.8 _"
עיין היטב בתוצאות הביצוע הבאות, המראות שתווי נקודה ורווח אינם נחשבים לתווי מילים:
regex = \w
input = aZ.8 _
Found [a] starting at 0 and ending at 0
Found [Z] starting at 1 and ending at 1
Found [8] starting at 3 and ending at 3
Found [_] starting at 5 and ending at 5
מפרידי שורות |
תיעוד ה-SDK של המחלקה Pattern מתאר את מט-תו הנקודה כמחלקת תו מוגדרת מראש התואמת לכל תו למעט מפרידי שורות (רצפים של תו אחד או שניים המסמנים את סוף השורה). היוצא מן הכלל הוא מצב dotall (עליו נדון בהמשך), שבו נקודות מתאימות גם למפרידי קווים. המחלקה Pattern מבדילה בין מפרידי השורות הבאים:
- תו החזרת כרכרה (
\r );
- תו newline (סמל לקידום נייר שורה אחת) (
\n );
- תו החזרת כרכרה מיד אחריו תו חדש (
\r\n );
- תו השורה הבאה (
\u0085 );
- תו מפריד שורות (
\u2028 );
- סמל מפריד פסקה (
\u2029 )
|
קבוצות שנלכדו
קבוצת הלכידה משמשת לשמירת קבוצת התווים שנמצאה לשימוש נוסף בעת חיפוש לפי דפוס. מבנה זה הוא רצף של תווים המוקפים במטא-תווים בסוגריים (
( )
). כל הדמויות בקבוצה שנלכדה נחשבות כמכלול אחד בעת חיפוש לפי דפוס. לדוגמה, קבוצת הלכידה (
Java
) משלבת את האותיות
J
,
a
,
v
ו
a
ליחידה אחת. קבוצת לכידה זו מוצאת את כל המופעים של התבנית
Java
בטקסט הקלט. עם כל התאמה, התווים המאוחסנים הקודמים
Java
מוחלפים בתווים הבאים. ניתן לקנן קבוצות שנתפסו בתוך קבוצות שנלכדו אחרות. לדוגמה, בביטוי רגולרי,
(Java( language))
קבוצה
(language)
מקוננת בתוך קבוצה
(Java)
. לכל קבוצת לכידה מקוננת או לא מקוננת מוקצה מספר, החל מ-1, והמספור עובר משמאל לימין. בדוגמה הקודמת,
(Java( language))
תואם את קבוצת הלכידה מספר 1 ומתאימה
(language)
לקבוצת הלכידה מספר 2. בביטוי הרגולרי
(a)(b)
,
(a)
מתאים לקבוצת הלכידה מספר 1 וקבוצת
(b)
הלכידה מספר 2.
ניתן לגשת מאוחר יותר להתאמות המאוחסנות על ידי קבוצות הלכידה באמצעות הפניות לאחור. הכוונה בתור תו קו נטוי ואחריו תו מספרי המתאים למספר הקבוצה הנלכדת, ההפניה לאחור מאפשרת להתייחס לתווים בטקסט שנלכדה על ידי הקבוצה. קישור נכנס גורם לתואם להתייחס לתוצאת החיפוש המאוחסנת של הקבוצה שנלכדה על סמך המספר ממנה, ולאחר מכן להשתמש בתווים מאותה תוצאה כדי לנסות חיפוש נוסף. הדוגמה הבאה מציגה את השימוש בהפניה לאחור כדי למצוא שגיאות דקדוקיות בטקסט:
java RegexDemo "(Java( language)\2)" "The Java language language"
דוגמה זו
(Java( language)\2)
משתמשת בביטוי רגולרי כדי למצוא שגיאה דקדוקית עם מילה כפולה
language
מיד אחריה
Java
בטקסט הקלט
"The Java language language"
. ביטוי רגולרי זה מציין שתי קבוצות ללכוד: מספר 1 –
(Java( language)\2)
, מתאים
Java language language
ומספר 2 –
(language)
, המתאים לתו הרווח ואחריו
language
. ההפניה לאחור
\2
מאפשרת לראות מחדש את התוצאה המאוחסנת של קבוצה מספר 2 כך שהמתאים יוכל לחפש את המופע השני של רווח ואחריו
language
, מיד לאחר המופע הראשון של רווח ו-
language
. תוצאות השידוך
RegexDemo
הן כדלקמן:
regex = (Java( language)\2)
input = The Java language language
Found [Java language language] starting at 4 and ending at 25
תואמי גבולות
לפעמים צריך לבצע התאמת דפוס בתחילת שורה, בגבולות מילים, בסוף טקסט וכו'. אתה יכול לעשות זאת על ידי שימוש באחד מתאמת הקצה של הכיתה
Pattern
, שהם מבני ביטוי רגולרי שמחפשים התאמות במיקומים הבאים:
^
: תחילת קו;
$
: סוף שורה;
\b
: גבול מילים;
\B
: גבול פסאודו-מילה;
\A
: תחילת טקסט;
\G
: סוף המשחק הקודם;
\Z
: סוף הטקסט, לא כולל מפריד קו נגרר (אם קיים);
\z
: סוף הטקסט
הדוגמה הבאה משתמשת
^
במת-תו מתאמת הגבול כדי למצוא שורות שמתחילות ב-
The
, ואחריהן אפס או יותר תווי מילה:
java RegexDemo "^The\w*" Therefore
התו
^
מציין ששלושת התווים הראשונים של טקסט הקלט חייבים להתאים לתווי דפוס עוקבים
T
,
h
ו-
e
, שלאחריהם ניתן למצוא כל מספר של סמלים יוצרי מילים. הנה התוצאה של הביצוע:
regex = ^The\w*
input = Therefore
Found [Therefore] starting at 0 and ending at 8
מה קורה אם תשנה את שורת הפקודה ל
java RegexDemo "^The\w*" " Therefore"
? לא תימצא התאמה מכיוון
Therefore
שלטקסט הקלט מקדים תו רווח.
התאמות באורך אפס
לפעמים, כשעובדים עם מתאמי קצה, תיתקלו בהתאמות באורך אפס.
Совпадение нулевой длины
הוא התאמה שאינה מכילה שום תווים. הם יכולים להתרחש בטקסט קלט ריק, בתחילת טקסט הקלט, אחרי התו האחרון של טקסט הקלט, ובין כל שני תווים של טקסט הקלט. קל לזהות התאמות באורך אפס מכיוון שהם תמיד מתחילים ומסתיימים באותו מיקום. שקול את הדוגמה הבאה:
java RegExDemo \b\b "Java is"
דוגמה זו מחפשת שני גבולות מילים עוקבים, והתוצאות נראות כך:
regex = \b\b
input = Java is
Found [] starting at 0 and ending at -1
Found [] starting at 4 and ending at 3
Found [] starting at 5 and ending at 4
Found [] starting at 7 and ending at 6
אנו רואים כמה התאמות באורך אפס בתוצאות. עמדות הסיום כאן הן אחת פחות מעמדות ההתחלה, מכיוון שציינתי
RegexDemo
בקוד המקור ברישום 1
end() – 1
.
מכמתים
מכמת הוא מבנה ביטוי רגולרי שמשייך באופן מפורש או מרומז דפוס לערך מספרי. ערך מספרי זה קובע כמה פעמים לחפש את התבנית. מכמתים מחולקים לחמדנים, עצלנים וסופר-חמדנים:
- הכמת החמדן (
?
, *
או +
) נועד למצוא את ההתאמה הארוכה ביותר. אני יכול לשאול X
? למצוא מופע אחד או פחות X
, X*
למצוא אפס התרחשות או יותר X
, X+
למצוא מופע אחד או יותר X
, X{n}
למצוא n
התרחשות X
, X{n,}
למצוא לפחות (ואולי יותר) n
מופעים , X
ולמצוא X{n,m}
לפחות התרחשות n
אך לא יותר .m
X
- מכמת העצל (
??
, *?
או +?
) נועד למצוא את ההתאמה הקצרה ביותר. אתה יכול לציין X??
לחיפוש מופע אחד או פחות של X
, X*
? למצוא אפס או יותר מופעים X
, X+?
למצוא מופע אחד או יותר X
, X{n}?
למצוא n
התרחשות X
, X{n,}?
למצוא לפחות (ואולי יותר) n
מופעים X
, X{n,m}?
ולמצוא לפחות n
אך לא יותר מאשר m
התרחשויות X
.
- הכמת החמדני-על (
?+
, *+
או ++
) דומה לכמת החמדן, אלא שהכמת החמדני-על עושה רק ניסיון אחד למצוא את ההתאמה הארוכה ביותר, בעוד שמכמת החמדן יכול לבצע מספר ניסיונות. ניתן להגדיר X?+
למצוא מופע אחד או פחות X
, X*+
למצוא אפס התרחשות או יותר X
, X++
למצוא מופע אחד או יותר X
, X{n}+
למצוא n
מופעים של X
, X{n,}+
למצוא לפחות (ואולי יותר) n
מופעים , X
ולמצוא X{n,m}+
לפחות n
אך לא יותר m
מהתרחשויות X
.
הדוגמה הבאה ממחישה את השימוש בכמת חמד:
java RegexDemo .*ox "fox box pox"
להלן התוצאות:
regex = .*ox
input = fox box pox
Found [fox box pox] starting at 0 and ending at 10
הכמת החמדן (
.*
) מוצא את הרצף הארוך ביותר של תווים המסתיים ב-
ox
. היא צורכת את כל טקסט הקלט ואז מתגלגלת לאחור עד שהיא מזהה שטקסט הקלט מסתיים בתווים אלה. שקול עכשיו את המכמת העצל:
java RegexDemo .*?ox "fox box pox"
התוצאות שלו:
regex = .*?ox
input = fox box pox
Found [fox] starting at 0 and ending at 2
Found [ box] starting at 3 and ending at 6
Found [ pox] starting at 7 and ending at 10
הכמת העצלן (
.*?
) מוצא את רצף התווים הקצר ביותר המסתיים ב-
ox
. זה מתחיל במחרוזת ריקה וצורך בהדרגה תווים עד שהוא מוצא התאמה. ולאחר מכן ממשיך לעבוד עד למיצוי טקסט הקלט. לבסוף, בואו נסתכל על המכמת הסופר-חמדן:
java RegexDemo .*+ox "fox box pox"
והנה התוצאות שלו:
regex = .*+ox
input = fox box pox
הכמת האקסטרה-greedy (
.*+
) לא מוצא התאמות מכיוון שהוא צורך את כל טקסט הקלט ולא נותר מה להתאים
ox
בסוף הביטוי הרגולרי. בניגוד לכמת החמדן, הכמת הסופר-חמדן אינו מתגלגל לאחור.
התאמות באורך אפס
לפעמים כשעובדים עם מכמים תיתקלו בהתאמות באורך אפס. לדוגמה, השימוש בכמת החמדן הבא מביא למספר התאמות באורך אפס:
java RegexDemo a? abaa
התוצאות של הפעלת דוגמה זו:
regex = a?
input = abaa
Found [a] starting at 0 and ending at 0
Found [] starting at 1 and ending at 0
Found [a] starting at 2 and ending at 2
Found [a] starting at 3 and ending at 3
Found [] starting at 4 and ending at 3
יש חמישה התאמות בתוצאות הביצוע. למרות שהראשון, השלישי והרביעי צפויים למדי (הם תואמים את המיקומים של שלוש אותיות
a
ב
abaa
), השני והחמישי עשויים להפתיע אותך. נראה כאילו הם מציינים מה
a
מתאים
b
לסוף הטקסט, אבל במציאות זה לא כך. הביטוי הרגולרי
a?
אינו מחפש
b
בסוף הטקסט. הוא מחפש נוכחות או היעדרות
a
. כאשר
a?
הוא לא מוצא
a
, הוא מדווח על כך כהתאמה באורך אפס.
ביטויי דגל מקוננים
מתאמים מייצרים כמה הנחות ברירת מחדל שניתן לעקוף אותם בעת קומפילציה של הביטוי הרגולרי לתבנית. נדון בנושא זה בהמשך. ביטוי רגולרי מאפשר לך לעקוף כל אחת מברירות המחדל באמצעות ביטוי דגל מקונן. מבנה ביטוי רגולרי זה מצוין כתו מטא של סוגריים סביב תו מטא של סימן שאלה (
?
), ואחריו אות לטינית קטנה. הכיתה
Pattern
מבינה את ביטויי הדגל המקוננים הבאים:
(?i)
: מאפשר התאמת דפוסים ללא רגישות רישיות. לדוגמה, בעת שימוש בפקודה, java RegexDemo (?i)tree Treehouse
רצף התווים Tree
תואם לתבנית tree
. ברירת המחדל היא חיפוש דפוס תלוי רישיות.
(?x)
: מאפשר שימוש בתווי רווח לבן והערות המתחילות באות המטא בתוך התבנית #
. השדכן יתעלם משניהם. לדוגמה, עבור java RegexDemo ".at(?x)#match hat, cat, and so on" matter
רצף של תווים mat
מתאים לתבנית .at
. כברירת מחדל, תווים והערות רווחים לבנים אינם מותרים, והמתאם מתייחס אליהם כאל תווים המעורבים בחיפוש.
(?s)
: מאפשר מצב dotall, שבו תו המטא תואם תואם למפרידי שורות בנוסף לכל תו אחר. לדוגמה, הפקודה java RegexDemo (?s). \n
תמצא תו חדש. ברירת המחדל היא ההפך מ-dotall: לא יימצאו מפרידי שורות. לדוגמה, הפקודה Java RegexDemo . \n
לא תמצא תו חדש.
(?m)
: מפעיל מצב ריבוי שורות, שבו הוא ^
מתאים להתחלה ולסוף $
של כל שורה. לדוגמה, java RegexDemo "(?m)^abc$" abc\nabc
מוצא את שני הרצפים בטקסט הקלט abc
. כברירת מחדל, נעשה שימוש במצב של שורה אחת: ^
תואם את ההתחלה של כל טקסט הקלט, ומתאים $
לסוף שלו. לדוגמה, java RegexDemo "^abc$" abc\nabc
מחזירה תגובה שאין התאמות.
(?u)
: מאפשר יישור רישיות רגיש ל-Unicode. דגל זה, בשימוש בשילוב עם (?i)
, מאפשר התאמת דפוסים ללא רגישות רישיות בהתאם לתקן Unicode. הגדרת ברירת המחדל היא חיפוש תווים תלויי רישיות ותווים US-ASCII בלבד.
(?d)
: מאפשר מצב מחרוזת בסגנון Unix, שבו המתאם מזהה תווים מטא בהקשר .
, ^
ורק $
את מפריד השורות \n
. ברירת המחדל היא מצב מחרוזת שאינה בסגנון יוניקס: המתאם מזהה, בהקשר של המטא-תווים לעיל, את כל תווי הקווים המפרידים.
ביטויי דגל מקוננים דומים לקבוצות שנתפסו מכיוון שהדמויות שלהם מוקפות במטא-תווים בסוגריים. בניגוד לקבוצות שנלכדו, ביטויי דגל מקוננים הם דוגמה לקבוצות שאינן נלכדות, שהן מבנה של ביטוי רגיל שאינו לוכד תווי טקסט. הם מוגדרים כרצפים של תווים מוקפים במטא-תווים של סוגריים.
ציון ביטויי דגל מקוננים מרובים |
ניתן לציין מספר ביטויי דגל מקוננים בביטוי רגולרי על ידי הצבתם זה לצד זה ( (?m)(?i)) ) או הצבת האותיות שמגדירות אותם ברצף ( (?mi) ). |
סיכום
כפי שבטח הבנתם עד עכשיו, ביטויים רגולריים הם שימושיים ביותר והופכים שימושיים עוד יותר ככל שאתם שולטים בניואנסים של התחביר שלהם. עד כה הצגתי לך את היסודות של ביטויים רגולריים וה-
Pattern
. בחלק 2, נבחן לעומק את ממשק ה-API של Regex ונחקור את השיטות של ה-
Pattern
,
Matcher
ו-
PatternSyntaxException
. אני גם אראה לך שני יישומים מעשיים של Regex API שתוכל להשתמש בהם באופן מיידי בתוכניות שלך.
ביטויים רגילים ב-Java, חלק 3 ביטויים רגילים ב-Java, חלק 4 ביטויים רגילים ב-Java, חלק 5
GO TO FULL VERSION