ביטוי רגולרי ב-Java – דוגמה ל Regex ב-Java

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

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

הביטוי הרגולרי ב-Java מגדיר תבנית עבור מחרוזת. ביטויים רגולריים ניתן להשתמש בהם לחיפוש, עריכה או עיבוד טקסט. ביטוי רגולרי אינו תלוי בשפה אך יש הבדילו קצת בין השפות. ביטויים רגולריים ב-Java הם הכי דומים לשפת Perl. כיתות ה-Regex ב-Java קיימות בחבילה java.util.regex המכילה שלוש כיתות:

  1. Pattern: אובייקט Pattern הוא הגרסה הממומנת של ביטוי הרגולרי. כיתת Pattern אין לה בנאי ציבורי ואנו משתמשים בשיטת ה-compile הציבורית שלה כדי ליצור את אובייקט ה-pattern על ידי מעבר של ארגומנט ביטוי הרגולרי.
  2. התאמן: Matcher הוא אובייקט מנוע ה-regex של ג'אווה שמתאים את דפוס המחרוזת הנתון עם אובייקט הדפוס שנוצר. מחלקת Matcher אין לה בנאי ציבורי ואנו מקבלים אובייקט Matcher באמצעות שיטת ה-matcher של אובייקט הדפוס שמקבלת את מחרוזת הקלט כארגומנט. אחר כך אנו משתמשים בשיטת matches שמחזירה תוצאת בוליאנית בהתבסס על תאימת מחרוזת הקלט לדפוס ה־regex או לא.
  3. PatternSyntaxException: PatternSyntaxException משולכת אם תחביר הביטוי הרגיל אינו נכון.

בואו נסתכל על דוגמת תוכנית regex של ג'אווה.

package com.journaldev.util;

import java.util.regex.*;

public class PatternExample {

	public static void main(String[] args) {
		Pattern pattern = Pattern.compile(".xx.");
		Matcher matcher = pattern.matcher("MxxY");
		System.out.println("Input String matches regex - "+matcher.matches());
		// ביטוי רגיל רע
		pattern = Pattern.compile("*xx*");

	}

}

כאשר אנו מפעילים את תוכנית דוגמת regex של ג'אווה זו, אנו מקבלים את הפלט הבא.

Input String matches regex - true
Exception in thread "main" java.util.regex.PatternSyntaxException: Dangling meta character '*' near index 0
*xx*
^
	at java.util.regex.Pattern.error(Pattern.java:1924)
	at java.util.regex.Pattern.sequence(Pattern.java:2090)
	at java.util.regex.Pattern.expr(Pattern.java:1964)
	at java.util.regex.Pattern.compile(Pattern.java:1665)
	at java.util.regex.Pattern.(Pattern.java:1337)
	at java.util.regex.Pattern.compile(Pattern.java:1022)
	at com.journaldev.util.PatternExample.main(PatternExample.java:13)

מאחר וביטוי הרגיל של ג'אווה נוגע למחרוזת, מחלקת String הוארכה בג'אווה 1.4 כדי לספק שיטת matches המבצעת תאמת דפוס regex. פנימית היא משתמשת במחלקות ה־Pattern וה־Matcher של ג'אווה כדי לבצע את העיבוד אך זאת ברור שזה מפחית את קווי הקוד. מחלקת Pattern מכילה גם את שיטת matches שמקבלת regex ומחרוזת קלט כארגומנט ומחזירה תוצאת בוליאנית לאחר התאמתם. לכן הקוד הבא עובד בצורה תקינה להתאמת מחרוזת הקלט לביטוי רגיל בג'אווה.

String str = "bbb";
System.out.println("Using String matches method: "+str.matches(".bb"));
System.out.println("Using Pattern matches method: "+Pattern.matches(".bb", str));

בכך שהדרישה שלך היא רק לבדוק אם מחרוזת הקלט תואמת את התבנית, כדאי לחסוך זמן וקוד באמצעות שימוש בשיטת התאמת מחרוזת פשוטה. יש להשתמש במחלקות Pattern ו-Matches רק כאשר יש צורך לעבד את מחרוזת הקלט או יש צורך להשתמש בתבנית מחדש. שים לב שהתבנית שמוגדרת על ידי regex נפעלת על מחרוזת הקלט משמאל לימין ופעם אחת שתווית המקור בשימוש בהתאמה, הם לא יכולים להיות בשימוש חוזר. לדוג, regex "121" יתאים "31212142121" רק פעמיים כמו "_121____121".

ביטויים רגולריים ב-Java – סמלים שכיחים להתאמה

Regular Expression Description Example
. Matches any single character (“…”, “a%”) – true(“…”, “.a”) – true (“…”, “a”) – false
^aaa Matches aaa regex at the beginning of the line (“^a.c.”, “abcd”) – true (“^a”, “ac”) – false
aaa$ Matches regex aaa at the end of the line (“…cd$”, “abcd”) – true(“a$”, “a”) – true (“a$”, “aca”) – false
[abc] Can match any of the letter a, b or c. [] are known as character classes. (“^[abc]d.”, “ad9”) – true(“[ab].d$”, “bad”) – true (“[ab]x”, “cx”) – false
[abc][12] Can match a, b or c followed by 1 or 2 (“[ab][12].”, “a2#”) – true(“[ab]…[12]”, “acd2”) – true (“[ab][12]”, “c2”) – false
[^abc] When ^ is the first character in [], it negates the pattern, matches anything except a, b or c (“[^ab][^12].”, “c3#”) – true(“[^ab]…[^12]”, “xcd3”) – true (“[^ab][^12]”, “c2”) – false
[a-e1-8] Matches ranges between a to e or 1 to 8 (“[a-e1-3].”, “d#”) – true(“[a-e1-3]”, “2”) – true (“[a-e1-3]”, “f2”) – false
xx yy Matches regex xx or yy

מטא-תווים ב-Regex של Java

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

Regular Expression Description
\d Any digits, short of [0-9]
\D Any non-digit, short for [^0-9]
\s Any whitespace character, short for [\t\n\x0B\f\r]
\S Any non-whitespace character, short for [^\s]
\w Any word character, short for [a-zA-Z_0-9]
\W Any non-word character, short for [^\w]
\b A word boundary
\B A non word boundary

ישנם שני דרכים להשתמש במטא-תווים כתווים רגילים בביטויים רגולריים.

  1. לפני המטא-תווים, הקדימות לתווים עם סלש (/).
  2. שמור על המטא-תווים בתוך \Q (שמתחיל בציטוט) ו-\E (שמסיים אותו).

ביטוי רגולרי ב-Java – קוונטיפיירים

קוונטיפיירים ב-Java Regex מציינים את מספר הופעות התו להתאמה.

Regular Expression Description
x? x occurs once or not at all
X* X occurs zero or more times
X+ X occurs one or more times
X{n} X occurs exactly n times
X{n,} X occurs n or more times
X{n,m} X occurs at least n times but not more than m times

קוונטיפיירים ב-Java Regex ניתפים לשימוש עם מחלקות תווים וקבוצות לכידה גם. לדוגמה, [abc]+ אומר – a, b או c – פעמים אחת או יותר. (abc)+ אומר קבוצת "abc" פעם אחת או יותר. נדבר עכשיו על קבוצת לכידה עכשיו.

ביטוי רגולרי ב-Java – קבוצות לכידה

קבוצות לכידה ביטוי רגולרי ב-Java משמשות לטיפול בתווים מרובים כיחידה אחת. ניתן ליצור קבוצה באמצעות (). החלק של מחרוזת הקלט שמתאים לקבוצת הלכידה מתווסף לזיכרון וניתן לקרוא אותו באמצעות התייחסות אחורית. ניתן להשתמש בשיטת matcher.groupCount כדי לגלות את מספר הקבוצות לכידה בתבנית regex של Java. לדוגמה, ((a)(bc)) מכיל 3 קבוצות לכידה – ((a)(bc)), (a) ו- (bc). ניתן להשתמש ב-התייחסות אחורית בביטוי הרגולרי עם חזרה (\) ואז מספר הקבוצה שיש לקרוא. קבוצות לכידה והתייחסויות אחוריות יכולות להיות מבלבלות, אז בואו נבין זאת בעזרת דוגמה.

System.out.println(Pattern.matches("(\\w\\d)\\1", "a2a2")); //true
System.out.println(Pattern.matches("(\\w\\d)\\1", "a2b2")); //false
System.out.println(Pattern.matches("(AB)(B\\d)\\2\\1", "ABB2B2AB")); //true
System.out.println(Pattern.matches("(AB)(B\\d)\\2\\1", "ABB2B3AB")); //false

בדוגמה הראשונה, ב-runtime, הקבוצה הראשונה שמופעלת היא (\w\d) שמשווה ל-"a2" כאשר היא מתאימה למחרוזת הקלט "a2a2" ומורשת בזיכרון. לכן, \1 מתייחסת ל-"a2" ולכן מחזירה אמת. בשל אותו הסיבה, ההצהרה השנייה מדפיסה שקר. נסה להבין את התרחשות זו להצהרות 3 ו-4 בעצמך. 🙂 עכשיו נסתכל על כמה מתודות חשובות של מחלקות Pattern ו- Matcher.

  1. ניתן ליצור אובייקט Pattern עם דגלים. לדוגמה, Pattern.CASE_INSENSITIVE מאפשר התאמה לא תלויה באותיות רישיות.
  2. מחלקת Pattern גם מספקת את המתודה split(String) שדומה למתודה split() של מחלקת String.
  3. מחלקת Pattern, באמצעות המתודה toString(), מחזירה מחרוזת ביטוי רגולרי ממנה התרגל תבנית זו.
  4. למחלקות Matcher יש מתודות אינדקס start() ו-end() שמציינות בדיוק איפה המתאימות נמצאות במחרוזת הקלט.
  5. מחלקת Matcher גם מספקת מתודות לניהול מחרוזות, כמו replaceAll(String replacement) ו-replaceFirst(String replacement).

בוא נסתכל על אלו מתודות regex בתכנית דוגמה פשוטה ב-Java.

package com.journaldev.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExamples {

	public static void main(String[] args) {
		// בשימוש בתבנית עם דגלים
		Pattern pattern = Pattern.compile("ab", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher("ABcabdAb");
		// בשימוש במתודות find(), group(), start() ו- end() של Matcher
		while (matcher.find()) {
			System.out.println("Found the text \"" + matcher.group()
					+ "\" starting at " + matcher.start()
					+ " index and ending at index " + matcher.end());
		}

		// בשימוש במתודה split() של Pattern
		pattern = Pattern.compile("\\W");
		String[] words = pattern.split("one@two#three:four$five");
		for (String s : words) {
			System.out.println("Split using Pattern.split(): " + s);
		}

		// בשימוש במתודות replaceFirst() ו- replaceAll() של Matcher
		pattern = Pattern.compile("1*2");
		matcher = pattern.matcher("11234512678");
		System.out.println("Using replaceAll: " + matcher.replaceAll("_"));
		System.out.println("Using replaceFirst: " + matcher.replaceFirst("_"));
	}

}

פלט של דוגמה זו של תכנית regex ב-Java הוא.

Found the text "AB" starting at 0 index and ending at index 2
Found the text "ab" starting at 3 index and ending at index 5
Found the text "Ab" starting at 6 index and ending at index 8
Split using Pattern.split(): one
Split using Pattern.split(): two
Split using Pattern.split(): three
Split using Pattern.split(): four
Split using Pattern.split(): five
Using replaceAll: _345_678
Using replaceFirst: _34512678

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

ניתן לבדוק קוד מלא ודוגמאות נוספות של ביטויים רגולריים ב-מאגר הקוד שלנו ב-GitHub.

Source:
https://www.digitalocean.com/community/tutorials/regular-expression-in-java-regex-example