قدرت بیشتر در عبارات باقاعده
تا اینجا فقط سه متد از کلاس String رو برای کار با عبارات باقاعده رو دیدیم. پکیج java.util.regex شامل سه کلاس برای پشتیبانی کامل از عبارات باقاعده است. این کلاسها عبارتند از:
- Pattern
- Matcher
- PatternSyntaxException
یه شئ از کلاس Pattern شامل فرم کامپایلشدهی یک عبارت باقاعده است. منظورم از فرم کامپایلشده، نوع خاصی از عبارت باقاعده است که توی حافظه ذخیره میشه تا عملیات مطابقت رشتهها رو سریعتر انجام بده. یه کلاس Matcher برای مرتبطکردن رشتهای که میخوایم بررسی کنیم، با یک شئ از کلاس Pattern بکار میره و عملیات بررسی تطبیق رو بعهده داره. اشیاء کلاس PatternSyntaxException هم بیانگر خطای پیشآمده داخل یه عبارات باقاعده هستن که قواعد نوشتن الگوها رو رعایت نکرده.
کامپایل عبارات باقاعده
یه شئ از کلاس Pattern که برای نگهداری فرم کامپایلشدهی یک عبارت باقاعده بکار میره، غیرقابل تغییر یا Immutable هست. این کلاس، سازندهی public نداره و فقط یه متد استاتیک بهاسم
compile() داره که شئ Pattern موردنظر رو برای عبارت باقاعدهی اعلامشده، برمیگردونه. این متد سربارگذاری شده:
- static Pattern compile(String regex)
- static Pattern compile(String regex, int flags)
به این تکه کد دقت کنید:
// Prepare a regular expression
String regex = "[a-z]@.";
// Compile the regular expression into a Pattern object
Pattern p = Pattern.compile(regex);
نسخهی دوم متد
compile() به شما اجازه میده یکسری پرچم (Flag) تعریف کنید که رفتار الگوی مورد مطابقت رو تغییر میده. این پرچمها ماسکهای بیتی هستن و میتونین برای ترکیبشون، اونها رو با همدیگه OR بیتی کنید (عملگر | اینکار رو انجام میده). به فهرست پرچمهای موجود دقت کنید:
- Pattern.CANON_EQ حالت برابری کانونی رو فعال میکنه. اگه این پرچم فعال بشه، دو کارکتر فقط درصورتی برابر هستن که تجزیهی کانونی اونها با هم برابر باشه.
- Pattern.CASE_INSENSITIVE حساسیت به بزرگی و کوچکی حروف رو از بین میبره. این حالت فقط روی کارکترهای US-ASCII کار میکنه و برای ازبینبردن حساسیت روی کارکترهای یونیکد، باید پرچم UNICODE_CASE رو هم با این پرچم ترکیب کنید.
- Pattern.COMMENTS اجازهی استفاده از کارکترهای فاصلهی خالی و توضیحات رو داخل الگو میده. وقتی این پرچم فعال بشه، کارکترهای Whitescape نادیده گرفته میشن و توضیحاتی که با # در انتهای هر خط از الگو مشخص میشن هم درنظر گرفته نخواهد شد.
- Pattern.DOTALL حالت پشتیبانی کامل از کارکترها رو برای متاکارکتر . (نقطه) فعال میکنه. بطور پیشفرض این متاکارکتر شامل کارکترهای انتهای سطر نمیشه؛ ولی وقتی این پرچم فعال بشه، شامل کارکترهای انتهای سطر هم خواهد شد.
- Pattern.LITERAL حالت پردازش حرفی رو فعال میکنه. وقتی این پرچم فعال بشه، کارکترهای داخل عبارت باقاعده بصورت حرفی درنظر گرفته میشن؛ یعنی متاکارکترها و کارکترهای خنثیسازی (Escape) هیچ معنای خاصی ندارن. البته پرچمهای CASE_INSENSITIVE و UNICODE_CASE تأثیر خودشون رو درصورتی که با این پرچم ترکیب بشن، همچنان خواهند داشت.
- Pattern.MULTILINE حالت چندسطری رو فعال میکنه. بطور پیشفرض متاکارکترهای ^ و $ بهمعنای ابتدا و انتهای کل متن ورودی هستن. وقتی این پرچم فعال بشه، این متاکارکترها بهمعنای ابتدا و انتهای هر سطر خواهند بود.
- Pattern.UNICODE_CASE حالت تشخیص بزرگی و کوچکی حروف رو در کارکترهای Unicode فعال میکنه. اگه این پرچم درکنار CASE_INSENSITIVE فعال بشه، عملیات حذف حساسیت به بزرگی و کوچکی حروف برمبنای استاندارد یونیکد انجام خواهد شد.
- Pattern.UNICODE_CHARACTER_CLASS نسخهی یونیکد کلاسهای کارکتر ازقبل تعریفشده و POSIX رو فعال میکنه. فعالکردن این پرچم تأثیر مشابه فعالکردن UNICODE_CASE رو هم خواهد داشت. وقتی این پرچم رو فعال کنید، کلاسهای ازقبل تعریفشده (فقط US-ASCII) و همچنین کلاسهای کارکتر POSIX، با استاندارد شمارهی 18 مستندات فنی یونیکد مطابقت پیدا میکنن (درمورد این استانداردها بعداً توضیح میدم).
- Pattern.UNIX_LINES حالت خطوط لینوکس رو فعال میکنه. وقتی این پرچم فعال میشه، فقط کارکتر n بعنوان پایان خط تشخیص داده میشه.
این تکه کد نحوهی کامپایل یه عبارت باقاعده رو با کمک پرچمهای CASE_INSENSITIVE و DOTALL نشون میده. بنابراین عملیات تطبیق رشته، بدون حساسیت به بزرگی و کوچکی حروف استاندارد US-ASCII انجام میشه و متاکارکتر نقطه شامل کارکترهای انتهای سطر هم خواهد بود. برای مثال رشتهی "A@n" با این الگو مطابقت خواهد داشت:
// Prepare a regular expression
String regex = "[a-z]@.";
// Compile the regular expression into a Pattern object and setting
// CASE_INSENSITIVE and DOTALL flags
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
ایجاد یک تطبیقدهنده
یک شئ از کلاس Matcher برای انجام بررسی مطابقت یک رشته از کارکترها با کمک الگوی کامپایلشدهای که توی یه شئ Pattern قرار گرفته، بکار میره. این کلاس هم سازندهی public نداره و برای بدستآوردن یک شئ از این کلاس، باید از متد
matcher() روی شئ Pattern موردنظر استفاده کنیم. این متد، رشتهای رو که میخوایم مطابقت رو روی اون مورد بررسی قرار بدیم، بعنوان پارامتر میگیره:
// Create a Pattern object
String regex = "[a-z]@.";
Pattern p = Pattern.compile(regex);
// String to perform the match
String str = "abc@yahoo.com,123@cnn.com,admin@ncis.ir";
// Get a Matcher object using Pattern object p for str
Matcher m = p.matcher(str);
در این مرحله، شئ Matcher ما یعنی m با الگوی ارائهشده در شئ Pattern یعنی p و متن موجود در str ارتباط پیدا کرده و الآن آمادهی بررسی مطابقت هستیم. معمولاً یه شئ Matcher برای پیداکردن مطابقت داخل مجموعهای از کارکترها بکار میره. این مطابقت ممکنه با موفقیت انجام بشه یا اینکه رشتهی مربوطه با الگوی موردنظر تطبیق پیدا نکنه. اگه تطبیق انجام بشه، ممکنه دوست داشته باشین محل شروع و پایان مطابقت و همچنین بخشی از متن که با الگوی مشخصشده مطابقت داشته رو بدونین. میتونین از شئ Matcher درخواست کنید که همهی این اطلاعات رو در اختیار شما بگذاره.
بررسی تطبیق الگو
برای اینکه از شئ Matcher بخواین عملیات مطابقت رو بررسی کنه، نیاز به استفاده از این متدها دارین:
- متد find()
- متد start()
- متد end()
- متد group()
متد
find() برای پیداکردن یک مورد تطابق الگو در داخل متن موردنظر بکار میره. اگه مطابقت انجام شد، نتیجهی true میده و درغیر اینصورت خروجی این متد false خواهد بود. اولین فراخوانی این متد شروع به جستجو از ابتدای متن میکنه. اگه فراخوانی قبلی این متد موفقیتآمیز باشه، فراخوانی مجددش باعث ادامهی بررسی از محل آخرین تطابق پیداشده میشه. معمولاً فراخوانی متد
find() داخل حلقه while انجام میشه تا همهی موارد مطابقت رو پیدا کنیم. این متد، سربارگذاری شده. نسخهی دیگری از متد
find() هم وجود داره که یه عدد صحیح بعنوان پارامتر میگیره که بیانگر فاصلهی موردنظر از ابتدای رشته برای شروع عملیات جستجو هست.
متد
start() محل شروع آخرین مطابقت رو برمیگردونه. شمارهگذاری کارکترها از صفر شروع میشه و معمولاً این متد بعد از یک فراخوانی موفقیتآمیز متد
find() صدا زده میشه.
متد
end() برای بدستآوردن محل پایان مطابقت + 1 بکار میره (درواقع محل اولین کارکتر بعد از مطابقت رو برمیگردونه). بنابراین، بعد از فراخوانی موفقیتآمیز متد
find()، تفاضل بین مقادیر خروجی
start() و
end() به شما طول رشتهای که با الگو مطابقت داشته رو برمیگردونه و باکمک متد
substring() از کلاس String روی شئ مربوط به رشتهی موردنظر، میتونین الگوی پیداشده رو استخراج کنید:
// Continued from previous fragment of code
if (m.find()) {
// str is the string we are looking into
String foundStr = str.substring(m.start(), m.end() - m.start());
}
متد
group() رشتهی پیداشده از آخرین فراخوانی موفقیتآمیز
find() رو برمیگردونه. یادتون نره که میشه باکمک متد
substring() هم اینکار رو انجام بدین و از متدهای
start() و
end() کمک بگیرین. بنابراین، کد بالا رو میشه اینطوری هم نوشت:
if (m.find()) {
String foundStr = m.group();
}
حالا به این مثال دقت کنید که نحوهی استفاده از این متدها رو نشون میده. اعتبارسنجی پارامترهای متدها برای سادگی و خوانایی برنامه نادیده گرفته شده. هدف از این کد، بررسی الگوی
"[abc]@." داخل رشتههای مختلفه:
public class PatternMatcher {
public static void findPattern(String regex, String source) {
// Compile regex into a Pattern object
Pattern p = Pattern.compile(regex);
// Get a Matcher object
Matcher m = p.matcher(source);
// Print regex and source text
System.out.println("nRegex: " + regex);
System.out.println("Text: " + source);
// Perform find
boolean found = false;
while (m.find()) {
System.out.println("Matched Text: " + m.group() + ", Start: " + m.start() + ", End: " + m.end());
// We found at least one match. Set the found variable to true
found = true;
}
if (!found) {
// We did not find any match
System.out.println("No match found");
}
}
}
// Usage:
String regex = "[abc]@.";
String source = "aida@gmail.com is a valid email address";
PatternMatcher.findPattern(regex, source);
source = "admin@ncis.ir";
PatternMatcher.findPattern(regex, source);
source = "a@band@yea@u";
PatternMatcher.findPattern(regex, source);
source = "There is an @ sign here";
PatternMatcher.findPattern(regex, source);
/* Output:
Regex: [abc]@.
Text: aida@gmail.com is a valid email address
Matched Text: c@j, Start: 3, End: 6
Regex: [abc]@.
Text: admin@ncis.ir
No match found
Regex: [abc]@.
Text: a@band@yea@u
Matched Text: a@b, Start: 0, End: 3
Matched Text: a@u, Start: 9, End: 12
Regex: [abc]@.
Text: There is an @ sign here
No match found
*/