JavaRush /בלוג Java /Random-HE /ביטויים רגולריים ב-Java, חלק 3

ביטויים רגולריים ב-Java, חלק 3

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

פשט את משימות התכנות הנפוצות עם ה-API של Regex

בחלקים 1 ו-2 של מאמר זה, התוודעתם לביטויים רגולריים ול-Regex API. למדת על הכיתה Patternועברת על דוגמאות המדגימות מבני ביטוי רגולרי, מהתאמת תבניות פשוטה באמצעות מחרוזות מילוליות ועד להתאמה מורכבת יותר באמצעות טווחים, מתאמי גבולות ומכמתים. בחלק זה ובחלקים הבאים נשקול נושאים שלא כוסו בחלק הראשון, נלמד את השיטות המקבילות של השיעורים Pattern, Matcherו PatternSyntaxException. תלמד גם שני כלי עזר המשתמשים בביטויים רגולריים כדי להקל על בעיות תכנות נפוצות. הראשון מחלץ הערות מקוד לתיעוד. השני הוא ספרייה של קוד לשימוש חוזר שנועד לבצע ניתוח מילוני - מרכיב חיוני של אסמבלים, מהדרים ותוכנות דומות.

הורדת קוד המקור

אתה יכול לקבל את כל קוד המקור (שנוצר על ידי Jeff Friesen עבור JavaWorld) עבור יישומי ההדגמה במאמר זה מכאן .

לימוד ממשק API

Pattern, Matcherוהם PatternSyntaxExceptionשלושת המחלקות המרכיבות את ה-API של Regex. כל אחת מהן מספקת שיטות המאפשרות לך להשתמש בביטויים רגולריים בקוד שלך.

שיטות של כיתת Pattern

מופע של מחלקה Patternהוא ביטוי רגולרי מהול, המכונה גם דפוס. ביטויים רגולריים מורכבים כדי לשפר את הביצועים של פעולות התאמת דפוסים. השיטות הסטטיות הבאות תומכות בהידור.
  • Pattern compile(String regex)מרכיב את התוכן regexלייצוג ביניים המאוחסן בחדש Pattern. שיטה זו מחזירה הפניה לאובייקט אם היא מצליחה, או זורקת חריג PatternSyntaxExceptionאם מזוהה תחביר ביטוי רגולרי לא חוקי. כל אובייקט של המחלקה Matcherבשימוש Patternאו מוחזר מאובייקט זה משתמש בהגדרות ברירת המחדל שלו, כגון חיפוש תלוי רישיות. כדוגמה, קטע הקוד Pattern p = Pattern.compile("(?m)^\\."); יוצר אובייקט Patternהמאחסן ייצוג הידור של ביטוי רגולרי כדי להתאים מחרוזות שמתחילות בתו נקודה.

  • Pattern compile(String regex, int flags)פותר את אותה בעיה כמו Pattern compile(String regex), אבל תוך התחשבות flags: קבוצה של קבועי סיביות עבור דגלי סיביות מסוג OR. המחלקה Patternמכריזה על קבועים CANON_EQ, CASE_INSENSITIVE, COMMENTS, DOTALL, LITERAL, MULTILINE, UNICODE_CASE, UNICODE_CHARACTER_CLASS и UNIX_LINESשניתן לשלב באמצעות OR (לדוגמה, CASE_INSENSITIVE | DOTALL) ולהעביר אותם כארגומנט flags.

  • למעט CANON_EQ, LITERAL и UNICODE_CHARACTER_CLASS, קבועים אלו מהווים חלופה לביטויי הדגל המקוננים שהוצגו בחלק 1. אם נתקל בקבוע דגל אחר מאלה שהוגדרו במחלקה Pattern, השיטה Pattern compile(String regex, int flags) זורקת חריג java.lang.IllegalArgumentException. לדוגמה, Pattern p = Pattern.compile("^\\.", Pattern.MULTILINE);שווה ערך לדוגמה הקודמת, כאשר הביטוי הקבוע Pattern.MULTILINEוביטוי הדגל המקונן (?m)עושים את אותו הדבר.
לפעמים יש צורך להשיג עותק של המחרוזת המקורית של ביטוי רגולרי שהורכב לאובייקט Pattern, יחד עם הדגלים שבהם הוא משתמש. כדי לעשות זאת, אתה יכול לקרוא לשיטות הבאות:
  • String pattern()מחזירה את מחרוזת הביטוי הרגולרי המקורית שהורכבה ל- Pattern.

  • int flags()מחזירה את דגלי האובייקט Pattern.
לאחר קבלת האובייקט Pattern, הוא משמש בדרך כלל להשגת האובייקט Matcherלביצוע פעולות התאמת דפוסים. השיטה Matcher matcher(Charsequence input)יוצרת אובייקט Matcherשמחפש בטקסט inputהתאמה לתבנית אובייקט Pattern. כאשר נקרא, הוא מחזיר הפניה לאובייקט זה Matcher. לדוגמה, הפקודה Matcher m = p.matcher(args[1]);מחזירה Matcherעבור האובייקט Patternשאליו המשתנה מפנה p.
חיפוש חד פעמי
שיטת static boolean matches(String regex, CharSequence input)המחלקה Patternמאפשרת לחסוך ביצירת אובייקטים Patternוחיפוש Matcherחד פעמי באמצעות תבנית. שיטה זו מחזירה true אם inputהדפוס מותאם regex, אחרת היא מחזירה false. אם הביטוי הרגולרי מכיל שגיאת תחביר, השיטה זורקת חריגה PatternSyntaxException. לדוגמה, System.out.println(Pattern.matches("[a-z[\\s]]*", "all lowercase letters and whitespace only"));מדפיס true, המאשר שהביטוי all lowercase letters and whitespace onlyמכיל רק רווחים ותווים קטנים.
ביטויים רגולריים ב-Java, חלק 3 - 2

פיצול טקסט

רוב המפתחים כתבו לפחות פעם אחת קוד כדי לפצל טקסט קלט לחלקים המרכיבים שלו, כמו המרת חשבון עובד מבוסס טקסט לקבוצה של שדות. הכיתה Patternמספקת את היכולת לפתור בצורה נוחה יותר את המשימה המייגעת הזו באמצעות שתי שיטות פיצול טקסט:
  • השיטה String[] split(CharSequence text, int limit)מתפצלת textלפי ההתאמות שנמצאו לתבנית האובייקט Patternומחזירה את התוצאות במערך. כל רכיב מערך מציין רצף טקסט המופרד מהרצף הבא על ידי קטע טקסט תואם דפוס (או סוף טקסט). הרכיבים של המערך נמצאים באותו סדר שבו הם מופיעים ב text.

    בשיטה זו, מספר רכיבי המערך תלוי בפרמטר limit, אשר שולט גם במספר ההתאמות שיימצאו.

    • ערך חיובי מחפש לא יותר מהתאמות limit-1ואורך המערך אינו עולה על limitאלמנטים.
    • אם הערך שלילי, כל ההתאמות האפשריות מתבצעות, ואורך המערך יכול להיות שרירותי.
    • אם הערך הוא אפס, כל ההתאמות האפשריות מתבצעות, אורך המערך יכול להיות שרירותי, ושורות ריקות בסוף נמחקות.

  • השיטה String[] split(CharSequence text)קוראת למתודה הקודמת עם 0 כארגומנט הגבול ומחזירה את תוצאת הקריאה שלה.
להלן תוצאות השיטה split(CharSequence text)לפתרון בעיית פיצול חשבון עובד לשדות נפרדים של שם, גיל, כתובת דואר ומשכורת:
Pattern p = Pattern.compile(",\\s");
String[] fields = p.split("John Doe, 47, Hillsboro Road, 32000");
for (int i = 0; i < fields.length; i++)
   System.out.println(fields[i]);
הקוד למעלה מתאר ביטוי רגולרי למציאת תו פסיק מיד ואחריו תו רווח בודד. להלן תוצאות ביצועו:
John Doe
47
Hillsboro Road
32000

פרדיקטים של תבניות וממשק ה-API של Streams

ב-Java 8, Patternשיטה הופיעה במחלקה . שיטה זו יוצרת פרדיקט (פונקציה בעלת ערך בוליאני) המשמשת כדי להתאים את התבנית. השימוש בשיטה זו מוצג בקטע הקוד הבא: Predicate asPredicate()
List progLangs = Arrays.asList("apl", "basic", "c", "c++", "c#", "cobol", "java", "javascript", "perl", "python", "scala");
Pattern p = Pattern.compile("^c");
progLangs.stream().filter(p.asPredicate()).forEach(System.out::println);
קוד זה יוצר רשימה של שמות שפות תכנות, ולאחר מכן מרכיב דפוס כדי למצוא את כל השמות שמתחילים באות c. שורת הקוד האחרונה לעיל מיישמת קבלת זרם סדרתי של נתונים עם רשימה זו כמקור. הוא מגדיר מסנן באמצעות פונקציה בוליאנית asPredicate()שמחזירה אמת כשהשם מתחיל באות cוחוזרת דרך הזרם, מדפיסה שמות תואמים לפלט סטנדרטי. שורה אחרונה זו מקבילה ללולאה הרגילה הבאה, המוכרת מאפליקציית RegexDemo מחלק 1:
for (String progLang: progLangs)
   if (p.matcher(progLang).find())
      System.out.println(progLang);

שיטות מחלקות התאמה

מופע של המחלקה Matcherמתאר מנגנון לביצוע פעולות התאמת דפוסים על רצף של תווים על ידי פרשנות הביטוי הרגולרי המהודר של המחלקה Pattern. אובייקטים של המחלקה Matcherתומכים בסוגים שונים של פעולות חיפוש דפוסים:
  • השיטה boolean find()מחפשת בטקסט הקלט עבור ההתאמה הבאה. שיטה זו מתחילה בסריקה בתחילת הטקסט שצוין או בתו הראשון לאחר ההתאמה הקודמת. האפשרות השנייה אפשרית רק אם הקריאה הקודמת לשיטה זו החזירה true והפותר לא אופס. בכל מקרה, אם החיפוש מצליח, הערך הבוליאני true מוחזר. דוגמה לשיטה זו ניתן למצוא בחלק RegexDemo1.

  • השיטה boolean find(int start)מאפסת את המתאים ומחפשת בטקסט את ההתאמה הבאה. הצפייה מתחילה מהמיקום שצוין על ידי הפרמטר start. אם החיפוש מצליח, הערך הבוליאני true מוחזר. לדוגמה, m.find(1);סורק את הטקסט החל ממיקום 1(מתעלמים ממיקום 0). אם הפרמטר startמכיל ערך שלילי או ערך גדול מאורך טקסט התואם, השיטה זורקת חריגה java.lang.IndexOutOfBoundsException.

  • השיטה boolean matches()מנסה להתאים את כל הטקסט לתבנית. הוא מחזיר ערך בוליאני true אם כל הטקסט תואם לתבנית. לדוגמה, הקוד Pattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.matches());יוצא falseמכיוון שהתו !אינו תו מילה.

  • השיטה boolean lookingAt()מנסה להתאים את הטקסט שצוין לתבנית. שיטה זו מחזירה אמת אם חלק כלשהו מהטקסט תואם לתבנית. שלא כמו השיטה matches();, כל הטקסט לא חייב להתאים לתבנית. לדוגמה, Pattern p = Pattern.compile("\\w*"); Matcher m = p.matcher("abc!"); System.out.println(p.lookingAt());הוא יפיק פלט true, מכיוון שתחילת הטקסט abc!מורכבת רק מתווים יוצרי מילים.

שלא כמו אובייקטי כיתה Pattern, אובייקטי כיתה Matcherשומרים על מידע מצב. לפעמים ייתכן שיהיה עליך לאפס את התאמת כדי לנקות מידע זה לאחר סיום חיפוש הדפוס. השיטות הבאות זמינות לאיפוס הפותר:
  • השיטה Matcher reset()מאפסת את מצב המתאם, כולל המיקום שיש להוסיף לסוף (איפוס ל-0). פעולת חיפוש הדפוס הבאה מתחילה בתחילת הטקסט המתאים. מחזירה הפניה לאובייקט הנוכחי Matcher. לדוגמה, m.reset();מאפס את הפותר שאליו מתייחס m.

  • השיטה Matcher reset(CharSequence text)מאפסת את מצב הפותר ומגדירה את טקסט הפותר החדש ל text. פעולת חיפוש הדפוס הבאה מתחילה בתחילת טקסט התאמת החדש. מחזירה הפניה לאובייקט הנוכחי Matcher. לדוגמה, m.reset("new text");מאפס את הפותר המפנה mומגדיר את טקסט הפותר החדש ל "new text".

ביטויים רגולריים ב-Java, חלק 3 - 3

הוספת טקסט לסוף

המיקום של התואם שיש להוסיף לסוף מציין את תחילת הטקסט המתואם שצורף לסוף אובייקט הסוג java.lang.StringBuffer. השיטות הבאות משתמשות בעמדה זו:
  • השיטה Matcher appendReplacement(StringBuffer sb, String replacement)קוראת את תווי הטקסט המתואם ומצרפת אותם לסוף האובייקט StringBufferשאליו מתייחס הארגומנט sb. שיטה זו מפסיקה לקרוא את התו האחרון שלפני התאמת התבנית הקודמת. לאחר מכן, השיטה מוסיפה את התווים מהאובייקט מהסוג Stringשאליו מתייחס הארגומנט replacementלסוף האובייקט StringBuffer(המחרוזת replacementעשויה להכיל הפניות לרצפי טקסט שנלכדו במהלך החיפוש הקודם; אלה מצוינים באמצעות התווים ($)ומספרי הקבוצות הנלכדים). לבסוף, השיטה מגדירה את הערך של מיקום התואם שיצורף למיקום התו האחרון המותאם פלוס אחד, ולאחר מכן מחזירה הפניה למשואם הנוכחי.

  • השיטה Matcher appendReplacement(StringBuffer sb, String replacement)זורקת חריגה java.lang.IllegalStateExceptionאם התואם עדיין לא מצא התאמה או שניסיון חיפוש קודם נכשל. זה זורק חריג IndexOutOfBoundsExceptionאם השורה replacementמציינת קבוצת לכידה שאינה בתבנית).

  • השיטה StringBuffer appendTail(StringBuffer sb)מוסיפה את כל הטקסט לאובייקט StringBufferומחזירה הפניה לאותו אובייקט. לאחר הקריאה למתודה האחרונה appendReplacement(StringBuffer sb, String replacement), קרא למתודה appendTail(StringBuffer sb)כדי להעתיק את הטקסט הנותר לאובייקט StringBuffer.

קבוצות שנלכדו
כפי שאתה זוכר מחלק 1, קבוצת לכידה היא רצף של תווים מוקפים בסוגריים ( ()) תווים מטאתיים. מטרת המבנה הזה היא לאחסן את התווים שנמצאו לשימוש חוזר מאוחר יותר במהלך התאמת דפוסים. כל הדמויות מהקבוצה שנלכדה נחשבות כמכלול אחד במהלך חיפוש הדפוס.
הקוד הבא קורא ל- appendReplacement(StringBuffer sb, String replacement)and appendTail(StringBuffer sbכדי להחליף את כל המופעים של רצף התווים בטקסט המקור catב- caterpillar:
Pattern p = Pattern.compile("(cat)");
Matcher m = p.matcher("one cat, two cats, or three cats on a fence");
StringBuffer sb = new StringBuffer();
while (m.find())
   m.appendReplacement(sb, "$1erpillar");
m.appendTail(sb);
System.out.println(sb);
שימוש בקבוצה שנלכדה והפניה אליה בטקסט החלופי מורה לתוכנית להוסיף erpillarלאחר כל מופע של cat. התוצאה של ביצוע קוד זה נראית כך: one caterpillar, two caterpillars, or three caterpillars on a fence

מחליף טקסט

המחלקה Matcherמספקת לנו שתי שיטות להחלפת טקסט, משלימות ל- appendReplacement(StringBuffer sb, String replacement). באמצעות שיטות אלה, תוכל להחליף את המופע הראשון של [הטקסט שהוחלף] או את כל המופעים:
  • השיטה String replaceFirst(String replacement)מאפסת את ה-matcher, יוצרת אובייקט חדש String, מעתיקה את כל התווים של טקסט ה-matcher (עד ההתאמה הראשונה) למחרוזת זו, מוסיפה את התווים לסוף שלה replacement, מעתיקה את שאר התווים למחרוזת ומחזירה אובייקט String(המחרוזת replacementיכולה להכיל הפניות לאלו שנלכדו במהלך רצפי טקסט החיפוש הקודמים באמצעות סמלי דולרים ומספרי קבוצה שנלכדו).

  • השיטה String replaceAll(String replacement)פועלת בדומה לשיטה String replaceFirst(String replacement), אך מחליפה replacementאת כל ההתאמות שנמצאו בתווים מהמחרוזת.

ביטוי רגולרי \s+מחפש תו רווח אחד או יותר בטקסט הקלט. להלן, נשתמש בביטוי הרגולרי הזה ונקרא לשיטה replaceAll(String replacement)להסרת רווחים כפולים:
Pattern p = Pattern.compile("\\s+");
Matcher m = p.matcher("Удаляем      \t\t лишние пробелы.   ");
System.out.println(m.replaceAll(" "));
להלן התוצאות: Удаляем лишние пробелы. ביטויים רגילים ב-Java, חלק 4 ביטויים רגילים ב-Java, חלק 5
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION