מהו ביטוי רגולרי של RegEx?
למעשה, ביטוי רגולרי (RegEx ב-Java) הוא דפוס לחיפוש מחרוזת בטקסט. ב-Java, הייצוג הראשוני של דפוס זה הוא תמיד מחרוזת, כלומר אובייקט של המחלקה String. עם זאת, לא ניתן להרכיב כל מחרוזת לביטוי רגולרי, אלא רק כאלו שעוקבים אחר הכללים לכתיבת ביטוי רגולרי - התחביר המוגדר במפרט השפה. לכתיבת ביטוי רגולרי משתמשים בתווים אלפביתיים ומספריים וכן תווים מטא - תווים בעלי משמעות מיוחדת בתחביר של ביטויים רגולריים. לדוגמה:String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;
יצירת ביטויים רגולריים ב-Java
כדי ליצור RegEx ב-Java, עליך לבצע שני שלבים פשוטים:- כתוב את זה כמחרוזת באמצעות תחביר ביטוי רגולרי;
- הידור מחרוזת זו לביטוי רגולרי;
Pattern
. כדי לעשות זאת, עליך לקרוא לאחת משתי השיטות הסטטיות הזמינות במחלקה compile
. השיטה הראשונה לוקחת ארגומנט אחד - מחרוזת מילולית של ביטוי רגולרי, והשנייה - בתוספת פרמטר נוסף שמפעיל את המצב להשוואת התבנית עם טקסט:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
רשימת ערכי הפרמטרים האפשריים flags
מוגדרת במחלקה Pattern
וזמינה לנו כמשתני מחלקה סטטיים. לדוגמה:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//searching for matches with the pattern will be done case-insensitively.
בעיקרו של דבר, המחלקה Pattern
היא בונה ביטוי רגולרי. מתחת למכסה המנוע, השיטה compile
קוראת לבנאי הפרטי של המחלקה Pattern
כדי ליצור תצוגה הידור. שיטה זו ליצירת מופע תבנית מיושמת במטרה ליצור אותו כאובייקט בלתי ניתן לשינוי. בעת היצירה, מתבצעת בדיקת תחביר של הביטוי הרגולרי. אם יש שגיאות בשורה, נוצר חריג PatternSyntaxException
.
תחביר ביטוי רגולרי
תחביר ביטוי רגולרי מבוסס על שימוש בסמלים<([{\^-=$!|]})?*+.>
, אותם ניתן לשלב עם תווים אלפביתיים. בהתאם לתפקידם, ניתן לחלק אותם למספר קבוצות:
מטא-תווים | מַטָרָה |
---|---|
^ | תחילת השורה |
$ | סוף השורה |
\ב | גבול מילים |
\B | לא הגבלה של מילים |
\א | תחילת הקלט |
\G | סוף המשחק הקודם |
\Z | סוף הקלט |
\z | סוף הקלט |
מטא-תווים | מַטָרָה |
---|---|
\ד | סמל דיגיטלי |
\D | תו לא מספרי |
\s | תו רווח |
\S | תו ללא רווח לבן |
\w | תו אלפאנומרי או קו תחתון |
\W | כל תו מלבד אלפביתי, מספרי או קו תחתון |
. | כל דמות |
מטא-תווים | מַטָרָה |
---|---|
\t | תו הכרטיסייה |
\n | דמות חדשה |
\r | תו החזרת כרכרה |
\f | עבור לדף חדש |
\u0085 | תו השורה הבאה |
\u 2028 | תו מפריד שורות |
\u 2029 | סמל מפריד פסקאות |
מטא-תווים | מַטָרָה |
---|---|
[א ב ג] | כל אחד מהאמור לעיל (א, ב או ג) |
[^abc] | כל פרט מאלה המפורטים (לא a, b, c) |
[a-zA-Z] | מיזוג טווח (תווים בלטיניים a עד z אינם תלויי רישיות) |
[מודעה[mp]] | שרשור של תווים (א ל-ד ו-m ל-p) |
[az&&[def]] | מפגש של סמלים (סמלים d,e,f) |
[az&&[^bc]] | הפחתת תווים (תווים a, dz) |
מטא-תווים | מַטָרָה |
---|---|
? | אחד או חסר |
* | אפס או יותר פעמים |
+ | פעם אחת או יותר |
{נ} | n פעמים |
{נ,} | n פעמים או יותר |
{נ,מ} | לא פחות מ-n פעמים ולא יותר מ-m פעמים |
מצב כימת חמדן
תכונה מיוחדת של מכמים היא היכולת להשתמש בהם במצבים שונים: חמדנים, סופר חמדנים ועצלנים. מצב חמדנות במיוחד מופעל על ידי הוספת הסמל "+
" אחרי המכמת, והמצב העצל על ידי הוספת הסמל " ?
". לדוגמה:
"A.+a" // greedy mode
"A.++a" // over-greedy mode
"A.+?a" // lazy mode
בעזרת תבנית זו כדוגמה, הבה ננסה להבין כיצד מכמים פועלים במצבים שונים. כברירת מחדל, המכמת פועל במצב חמדני. זה אומר שהוא מחפש את ההתאמה הארוכה ביותר האפשרית במחרוזת. כתוצאה מהפעלת הקוד הזה:
public static void main(String[] args) {
String text = "Egor Alla Alexander";
Pattern pattern = Pattern.compile("A.+a");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println(text.substring(matcher.start(), matcher.end()));
}
}
נקבל את הפלט הבא: Alla Alexa אלגוריתם החיפוש עבור דפוס נתון " А.+а
" מבוצע ברצף הבא:
-
בתבנית הנתונה, התו הראשון הוא תו האות הרוסית
А
.Matcher
מתאים אותו לכל תו בטקסט, החל ממיקום אפס. במיקום אפס בטקסט שלנו יש סמלЕ
, כךMatcher
שהוא עובר על התווים בטקסט ברצף עד שהוא פוגש התאמה לתבנית. בדוגמה שלנו, זה הסמל בעמדה מס' 5. -
לאחר שנמצא התאמה עם התו הראשון של הדפוס,
Matcher
הוא בודק את ההתאמה עם התו השני של הדפוס. במקרה שלנו, זהו הסמל ".
", המייצג כל תו.במיקום השישי נמצא סמל האות
л
. כמובן, זה תואם את דפוס "כל דמות". -
Matcher
עובר לבדיקת התו הבא מהתבנית. בתבנית שלנו, הוא מצוין באמצעות.+
מכמת " ". מכיוון שמספר החזרות של "כל תו" בתבנית הוא פעם אחת או יותר,Matcher
הוא לוקח בתורו את התו הבא מהמחרוזת ובודק את התאימות לדפוס, כל עוד מתקיים התנאי של "כל תו", בדוגמה שלנו - עד סוף השורה (מעמדה מס' 7 - מס' 18 של הטקסט).למעשה,
Matcher
הוא תופס את כל השורה עד הסוף - כאן מתבטא ה"חמדנות" שלו. -
לאחר
Matcher
שמגיעים לסוף הטקסט ומסיימים לבדוק אתА.+
החלק " " של התבנית, Matcher מתחיל לבדוק את שאר התבנית - תו האותа
. מכיוון שהטקסט בכיוון קדימה הסתיים, הסימון מתרחש בכיוון ההפוך, החל מהתו האחרון: -
Matcher
"זוכר" את מספר החזרות בתבנית ".+
" שבה הוא הגיע לסוף הטקסט, כך שהוא מקטין את מספר החזרות באחת ובודק את התבנית עבור הטקסט עד שנמצא התאמה:
מצב כימת חמד במיוחד
במצב חמד סופר, השידוך פועל בדומה למנגנון מצב חמד. ההבדל הוא שכאשר תופסים טקסט עד סוף השורה, אין חיפוש אחורה. כלומר, שלושת השלבים הראשונים במצב חמד סופר יהיו דומים למצב חמד. לאחר לכידת המחרוזת כולה, השדכן מוסיף את שאר התבנית ומשווה אותה למחרוזת שנלכדה. בדוגמה שלנו, בעת ביצוע השיטה הראשית עם התבנית "А.++а
", לא יימצאו התאמות.
מצב כימת עצלן
-
במצב זה, בשלב הראשוני, כמו במצב החמדני, מחפשים התאמה עם התו הראשון של התבנית:
-
לאחר מכן, הוא מחפש התאמה לדמות הבאה בתבנית - כל תו:
-
בניגוד למצב החמדני, מצב העצל מחפש את ההתאמה הקצרה ביותר בטקסט, כך שלאחר מציאת התאמה עם התו השני של התבנית, המצוין בנקודה ומתאימה לתו במיקום מס' 6 של הטקסט, הוא
Matcher
יבדוק אם הטקסט מתאים לשאר הדפוס - התו "а
" . -
מכיוון שלא נמצאה התאמה לתבנית בטקסט (בעמדה מס' 7 בטקסט יש את הסמל "
л
"),Matcher
הוא מוסיף עוד "כל תו" בתבנית, מכיוון שהוא מצוין כפעם אחת או יותר, ושוב משווה את התבנית עם הטקסט במקומות עם מס' 5 עד מס' 8: -
במקרה שלנו נמצאה התאמה, אך טרם הגיע סוף הטקסט. לכן, מעמדה מס' 9, הסימון מתחיל בחיפוש התו הראשון של התבנית באמצעות אלגוריתם דומה ולאחר מכן חוזר על עצמו עד סוף הטקסט.
main
בעת שימוש בתבנית " А.+?а
", נקבל את התוצאה הבאה: Alla Alexa כפי שניתן לראות מהדוגמה שלנו, כאשר משתמשים במצבי כימת שונים עבור אותה תבנית, קיבלנו תוצאות שונות. לכן, יש צורך לקחת את התכונה הזו בחשבון ולבחור את המצב הרצוי בהתאם לתוצאה הרצויה במהלך החיפוש.
תווים בורחים בביטויים רגולריים
מכיוון שביטוי רגולרי ב-Java, או ליתר דיוק הייצוג הראשוני שלו, מוגדר באמצעות string literal, יש צורך לקחת בחשבון את הכללים של מפרט Java המתייחסים ל-string literal. בפרט, התו האחורי "\
" במילולי מחרוזת בקוד המקור של Java מתפרש כתו בריחה שמתריע למהדר שהתו שאחריו הוא תו מיוחד ויש לפרש אותו בצורה מיוחדת. לדוגמה:
String s = "The root directory is \nWindows";//wrap Windows to a new line
String s = "The root directory is \u00A7Windows";//insert paragraph character before Windows
לכן, במילולי מחרוזת שמתארים ביטוי רגולרי ומשתמשים בתו " \
" (לדוגמה, עבור מטא-תווים), יש להכפיל אותו כך שהמהדר של Java bytecode לא יפרש אותו אחרת. לדוגמה:
String regex = "\\s"; // template for searching for space characters
String regex = "\"Windows\""; // pattern to search for the string "Windows"
יש להשתמש בתו האלכסון האחורי הכפול גם כדי לחמוק מתווים מיוחדים אם אנחנו מתכננים להשתמש בהם כתווים "רגילים". לדוגמה:
String regex = "How\\?"; // template for searching the string "How?"
שיטות של כיתת Pattern
למחלקהPattern
יש שיטות אחרות לעבודה עם ביטויים רגולריים: String pattern()
– מחזירה את ייצוג המחרוזת המקורי של הביטוי הרגולרי שממנו נוצר האובייקט Pattern
:
Pattern pattern = Pattern.compile("abc");
System.out.println(Pattern.pattern())//"abc"
static boolean matches(String regex, CharSequence input)
– מאפשר לך לבדוק את הביטוי הרגולרי המועבר בפרמטר regex מול הטקסט המועבר בפרמטר input
. מחזירה: true - אם הטקסט תואם לתבנית; שקר - אחרת; דוגמא:
System.out.println(Pattern.matches("A.+a","Alla"));//true
System.out.println(Pattern.matches("A.+a","Egor Alla Alexander"));//false
int flags()
- מחזירה את flags
ערכי פרמטר התבנית שהוגדרו בעת יצירתו, או 0 אם פרמטר זה לא הוגדר. דוגמא:
Pattern pattern = Pattern.compile("abc");
System.out.println(pattern.flags());// 0
Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
System.out.println(pattern.flags());// 2
String[] split(CharSequence text, int limit)
- מפצל את הטקסט המועבר כפרמטר למערך של אלמנטים String
. הפרמטר limit
קובע את המספר המרבי של התאמות שמחפשים בטקסט:
- כאשר - מתבצע
limit>0
חיפוש התאמות;limit-1
- at
limit<0
- מחפש את כל ההתאמות בטקסט - כאשר
limit=0
- מחפש את כל ההתאמות בטקסט, בעוד שורות ריקות בסוף המערך נמחקות;
public static void main(String[] args) {
String text = "Egor Alla Anna";
Pattern pattern = Pattern.compile("\\s");
String[] strings = pattern.split(text,2);
for (String s : strings) {
System.out.println(s);
}
System.out.println("---------");
String[] strings1 = pattern.split(text);
for (String s : strings1) {
System.out.println(s);
}
}
פלט מסוף: Egor Alla Anna -------- Egor Alla Anna נשקול שיטת מחלקה נוספת ליצירת אובייקט Matcher
למטה.
שיטות מחלקות התאמה
Matcher
היא מחלקה שממנה נוצר אובייקט לחיפוש תבניות. Matcher
– זהו "מנוע חיפוש", "מנוע" של ביטויים רגולריים. כדי לחפש, צריך לתת לו שני דברים: דפוס חיפוש ו"כתובת" לחפש בה. כדי ליצור אובייקט, Matcher
השיטה הבאה מסופקת במחלקה Pattern
: рublic Matcher matcher(CharSequence input)
כארגומנט, השיטה לוקחת רצף של תווים בהם יתבצע החיפוש. אלו הם אובייקטים של מחלקות שמיישמות את הממשק CharSequence
. אתה יכול להעביר לא רק , String
אלא גם StringBuffer
, וכוויכוח . תבנית החיפוש היא אובייקט המחלקה שעליו נקראת המתודה . דוגמה ליצירת תואם: StringBuilder
Segment
CharBuffer
Pattern
matcher
Pattern p = Pattern.compile("a*b");// compiled the regular expression into a view
Matcher m = p.matcher("aaaaab");//created a search engine in the text “aaaaab” using the pattern "a*b"
כעת, בעזרת "מנוע החיפוש" שלנו, נוכל לחפש התאמות, לגלות את מיקום ההתאמה בטקסט, ולהחליף את הטקסט באמצעות שיטות מחלקות. השיטה boolean find()
מחפשת את ההתאמה הבאה בטקסט עם התבנית. באמצעות שיטה זו ואופרטור הלולאה ניתן לנתח את הטקסט כולו לפי מודל האירוע (לבצע את הפעולות הדרושות בעת מתרחש אירוע - מציאת התאמה בטקסט). לדוגמה, באמצעות המתודות של מחלקה זו, int start()
ניתן int end()
לקבוע את מיקומי ההתאמה בטקסט, ובאמצעות המתודות String replaceFirst(String replacement)
, String replaceAll(String replacement)
להחליף את ההתאמות בטקסט בטקסט חלופי אחר. דוגמא:
public static void main(String[] args) {
String text = "Egor Alla Anna";
Pattern pattern = Pattern.compile("A.+?a");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
int start=matcher.start();
int end=matcher.end();
System.out.println("Match found" + text.substring(start,end) + " с "+ start + " By " + (end-1) + "position");
}
System.out.println(matcher.replaceFirst("Ira"));
System.out.println(matcher.replaceAll("Olga"));
System.out.println(text);
}
פלט התוכנית: נמצאה התאמה Alla מ-5 עד 8 עמדות נמצאה התאמה אנה מ-10 עד 13 עמדות Egor Ira Anna Egor Olga Olga Egor Alla Anna מהדוגמה ברור שהשיטות replaceFirst
יוצרות replaceAll
אובייקט חדש String
- מחרוזת, אשר הוא טקסט המקור שבו התאמות עם תבנית מוחלפות בטקסט המועבר למתודה כארגומנט. יתרה מכך, השיטה replaceFirst
מחליפה רק את ההתאמה הראשונה, ואת replaceAll
כל ההתאמות בבדיקה. הטקסט המקורי נשאר ללא שינוי. ניתן למצוא את השימוש בשיטות מחלקות אחרות Matcher
, כמו גם דוגמאות לביטויים רגולריים, בסדרת מאמרים זו . הפעולות הנפוצות ביותר עם ביטויים רגולריים בעבודה עם טקסט הן משיעורים Pattern
ומובנות Matcher
ב- String
. אלו הן שיטות כגון split
, matches
, replaceFirst
, replaceAll
. אבל למעשה, "מתחת למכסה המנוע" הם משתמשים ב- Pattern
ו Matcher
. לכן, אם אתה צריך להחליף טקסט או להשוות מחרוזות בתוכנית מבלי לכתוב קוד מיותר, השתמש בשיטות של String
. אם אתה צריך יכולות מתקדמות, חשוב על שיעורים Pattern
ו Matcher
.
סיכום
ביטוי רגולרי מתואר בתוכנת Java באמצעות מחרוזות התואמות לתבנית שהוגדרה על ידי הכללים. כאשר הקוד פועל, Java מרכיבה מחדש מחרוזת זו לאובייקט מחלקהPattern
ומשתמשת באובייקט המחלקה Matcher
כדי למצוא התאמות בטקסט. כפי שאמרתי בהתחלה, ביטויים רגולריים מונחים לעתים קרובות בצד למועד מאוחר יותר, נחשבים לנושא קשה. עם זאת, אם אתה מבין את היסודות של תחביר, מטא-תווים, בריחה ולומד דוגמאות של ביטויים רגולריים, הם מתגלים הרבה יותר פשוטים ממה שהם נראים במבט ראשון.
GO TO FULL VERSION