JavaRush /בלוג Java /Random-HE /ביטויים רגולריים ב-Java (RegEx)

ביטויים רגולריים ב-Java (RegEx)

פורסם בקבוצה
ביטויים רגולריים הם נושא שמתכנתים, אפילו מנוסים, דוחים לעתים קרובות למועד מאוחר יותר. עם זאת, רוב מפתחי Java יצטרכו במוקדם או במאוחר להתמודד עם עיבוד טקסט. לרוב - עם פעולות חיפוש בטקסט ועריכה. ללא ביטויים רגולריים, קוד תוכנית פרודוקטיבי וקומפקטי הקשור לעיבוד טקסט פשוט בלתי מתקבל על הדעת. אז תפסיקו לדחות את זה, בואו נתמודד עם ה"קבועים" כבר עכשיו. זו לא משימה כל כך קשה.

מהו ביטוי רגולרי של RegEx?

למעשה, ביטוי רגולרי (RegEx ב-Java) הוא דפוס לחיפוש מחרוזת בטקסט. ב-Java, הייצוג הראשוני של דפוס זה הוא תמיד מחרוזת, כלומר אובייקט של המחלקה String. עם זאת, לא ניתן להרכיב כל מחרוזת לביטוי רגולרי, אלא רק כאלו שעוקבים אחר הכללים לכתיבת ביטוי רגולרי - התחביר המוגדר במפרט השפה. לכתיבת ביטוי רגולרי משתמשים בתווים אלפביתיים ומספריים וכן תווים מטא - תווים בעלי משמעות מיוחדת בתחביר של ביטויים רגולריים. לדוגמה:
String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;

יצירת ביטויים רגולריים ב-Java

כדי ליצור RegEx ב-Java, עליך לבצע שני שלבים פשוטים:
  1. כתוב את זה כמחרוזת באמצעות תחביר ביטוי רגולרי;
  2. הידור מחרוזת זו לביטוי רגולרי;
עבודה עם ביטויים רגולריים בכל תוכנת 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.

תחביר ביטוי רגולרי

תחביר ביטוי רגולרי מבוסס על שימוש בסמלים <([{\^-=$!|]})?*+.>, אותם ניתן לשלב עם תווים אלפביתיים. בהתאם לתפקידם, ניתן לחלק אותם למספר קבוצות:
1. מטא-תווים להתאמת גבולות קו או טקסט
מטא-תווים מַטָרָה
^ תחילת השורה
$ סוף השורה
גבול מילים
\B לא הגבלה של מילים
תחילת הקלט
\G סוף המשחק הקודם
\Z סוף הקלט
\z סוף הקלט
2. מטא-תווים לחיפוש כיתות אופי
מטא-תווים מַטָרָה
סמל דיגיטלי
\D תו לא מספרי
\s תו רווח
\S תו ללא רווח לבן
\w תו אלפאנומרי או קו תחתון
\W כל תו מלבד אלפביתי, מספרי או קו תחתון
. כל דמות
3. מטא-תווים לחיפוש סמלים לעריכת טקסט
מטא-תווים מַטָרָה
\t תו הכרטיסייה
\n דמות חדשה
\r תו החזרת כרכרה
\f עבור לדף חדש
\u0085 תו השורה הבאה
\u 2028 תו מפריד שורות
\u 2029 סמל מפריד פסקאות
4. מטא-תווים לקיבוץ דמויות
מטא-תווים מַטָרָה
[א ב ג] כל אחד מהאמור לעיל (א, ב או ג)
[^abc] כל פרט מאלה המפורטים (לא a, b, c)
[a-zA-Z] מיזוג טווח (תווים בלטיניים a עד z אינם תלויי רישיות)
[מודעה[mp]] שרשור של תווים (א ל-ד ו-m ל-p)
[az&&[def]] מפגש של סמלים (סמלים d,e,f)
[az&&[^bc]] הפחתת תווים (תווים a, dz)
5. מטא-סמלים לציון מספר התווים - מכמתים. הכמת בא תמיד אחרי תו או קבוצת תווים.
מטא-תווים מַטָרָה
? אחד או חסר
* אפס או יותר פעמים
+ פעם אחת או יותר
{נ} 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 אלגוריתם החיפוש עבור דפוס נתון " А.+а" מבוצע ברצף הבא:
  1. בתבנית הנתונה, התו הראשון הוא תו האות הרוסית А. Matcherמתאים אותו לכל תו בטקסט, החל ממיקום אפס. במיקום אפס בטקסט שלנו יש סמל Е, כך Matcherשהוא עובר על התווים בטקסט ברצף עד שהוא פוגש התאמה לתבנית. בדוגמה שלנו, זה הסמל בעמדה מס' 5.

    ביטויים רגולריים ב-Java - 2
  2. לאחר שנמצא התאמה עם התו הראשון של הדפוס, Matcherהוא בודק את ההתאמה עם התו השני של הדפוס. במקרה שלנו, זהו הסמל " .", המייצג כל תו.

    ביטויים רגולריים ב-Java - 3

    במיקום השישי נמצא סמל האות л. כמובן, זה תואם את דפוס "כל דמות".

  3. Matcherעובר לבדיקת התו הבא מהתבנית. בתבנית שלנו, הוא מצוין באמצעות .+מכמת " ". מכיוון שמספר החזרות של "כל תו" בתבנית הוא פעם אחת או יותר, Matcherהוא לוקח בתורו את התו הבא מהמחרוזת ובודק את התאימות לדפוס, כל עוד מתקיים התנאי של "כל תו", בדוגמה שלנו - עד סוף השורה (מעמדה מס' 7 - מס' 18 של הטקסט).

    ביטויים רגולריים ב-Java - 4

    למעשה, Matcherהוא תופס את כל השורה עד הסוף - כאן מתבטא ה"חמדנות" שלו.

  4. לאחר Matcherשמגיעים לסוף הטקסט ומסיימים לבדוק את А.+החלק " " של התבנית, Matcher מתחיל לבדוק את שאר התבנית - תו האות а. מכיוון שהטקסט בכיוון קדימה הסתיים, הסימון מתרחש בכיוון ההפוך, החל מהתו האחרון:

    ביטויים רגולריים ב-Java - 5
  5. Matcher"זוכר" את מספר החזרות בתבנית " .+" שבה הוא הגיע לסוף הטקסט, כך שהוא מקטין את מספר החזרות באחת ובודק את התבנית עבור הטקסט עד שנמצא התאמה: ביטויים רגולריים ב-Java - 6

מצב כימת חמד במיוחד

במצב חמד סופר, השידוך פועל בדומה למנגנון מצב חמד. ההבדל הוא שכאשר תופסים טקסט עד סוף השורה, אין חיפוש אחורה. כלומר, שלושת השלבים הראשונים במצב חמד סופר יהיו דומים למצב חמד. לאחר לכידת המחרוזת כולה, השדכן מוסיף את שאר התבנית ומשווה אותה למחרוזת שנלכדה. בדוגמה שלנו, בעת ביצוע השיטה הראשית עם התבנית " А.++а", לא יימצאו התאמות. ביטויים רגולריים ב-Java - 7

מצב כימת עצלן

  1. במצב זה, בשלב הראשוני, כמו במצב החמדני, מחפשים התאמה עם התו הראשון של התבנית:

    ביטויים רגולריים ב-Java - 8
  2. לאחר מכן, הוא מחפש התאמה לדמות הבאה בתבנית - כל תו:

    ביטויים רגולריים ב-Java - 9
  3. בניגוד למצב החמדני, מצב העצל מחפש את ההתאמה הקצרה ביותר בטקסט, כך שלאחר מציאת התאמה עם התו השני של התבנית, המצוין בנקודה ומתאימה לתו במיקום מס' 6 של הטקסט, הוא Matcherיבדוק אם הטקסט מתאים לשאר הדפוס - התו " а" .

    ביטויים רגולריים ב-Java - 10
  4. מכיוון שלא נמצאה התאמה לתבנית בטקסט (בעמדה מס' 7 בטקסט יש את הסמל " л"), Matcherהוא מוסיף עוד "כל תו" בתבנית, מכיוון שהוא מצוין כפעם אחת או יותר, ושוב משווה את התבנית עם הטקסט במקומות עם מס' 5 עד מס' 8:

    ביטויים רגולריים ב-Java - 11
  5. במקרה שלנו נמצאה התאמה, אך טרם הגיע סוף הטקסט. לכן, מעמדה מס' 9, הסימון מתחיל בחיפוש התו הראשון של התבנית באמצעות אלגוריתם דומה ולאחר מכן חוזר על עצמו עד סוף הטקסט.

    ביטויים רגולריים ב-Java - 12
כתוצאה מהשיטה, 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, וכוויכוח . תבנית החיפוש היא אובייקט המחלקה שעליו נקראת המתודה . דוגמה ליצירת תואם: StringBuilderSegmentCharBufferPatternmatcher
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כדי למצוא התאמות בטקסט. כפי שאמרתי בהתחלה, ביטויים רגולריים מונחים לעתים קרובות בצד למועד מאוחר יותר, נחשבים לנושא קשה. עם זאת, אם אתה מבין את היסודות של תחביר, מטא-תווים, בריחה ולומד דוגמאות של ביטויים רגולריים, הם מתגלים הרבה יותר פשוטים ממה שהם נראים במבט ראשון.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION