Quantifier ها به شما امکان مشخص کردن تعداد دفعات تطبیق را میدهند. برای راحتی، سه بخش مشخصات API Pattern که Quantifier های Greedy، Reluctant و Possessive را توصیف میکنند در ادامه آمده است.
شاید در نگاه اول به نظر برسد Quantifier های X?، X?? و X?+ دقیقاً یک کار را انجام میدهند، چون هر سه «X، یکبار یا هرگز» را تطبیق میدهند. اما تفاوتهای ظریفی در پیادهسازی وجود دارد که در انتهای این بخش توضیح داده خواهد شد.
| Greedy (حریص) | Reluctant (بیمیل) | Possessive (مالکانه) | معنی |
|---|---|---|---|
X? | X?? | X?+ | X، یکبار یا هرگز |
X* | X*? | X*+ | X، صفر یا بیشتر بار |
X+ | X+? | X++ | X، یک یا بیشتر بار |
X{n} | X{n}? | X{n}+ | X، دقیقاً n بار |
X{n,} | X{n,}? | X{n,}+ | X، حداقل n بار |
X{n,m} | X{n,m}? | X{n,m}+ | X، حداقل n اما بیشتر از m بار نه |
بررسی Quantifier های Greedy را با ساخت سه عبارت باقاعدهی مختلف شروع میکنیم: حرف «a» به دنبال ؟، * یا +. ببینیم وقتی این عبارات روی رشتهی ورودی خالی «» آزمایش میشوند چه اتفاقی میافتد:
Enter your regex: a?
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a*
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a+
Enter input string to search:
No match found.
در مثال بالا، تطبیق در دو حالت اول موفق است چون عبارات a? و a* هر دو صفر بار تطبیق حرف a را مجاز میدانند. همچنین توجه کنید ایندکس شروع و پایان هر دو صفر است، برخلاف تمام مثالهای قبلی. رشتهی ورودی خالی «» طولی ندارد، بنابراین تست فقط در ایندکس ۰ با هیچچیز تطبیق میدهد. تطبیقهایی از این دست به نام تطبیقهای با طول صفر (Zero-Length Matches) شناخته میشوند.
تطبیق با طول صفر میتواند در چندین حالت رخ دهد:
تطبیقهای با طول صفر به راحتی قابل تشخیص هستند چون همیشه در یک موقعیت ایندکس شروع و پایان مییابند.
تطبیقهای با طول صفر را با چند مثال دیگر بررسی میکنیم. رشتهی ورودی را به یک حرف «a» تغییر دهید و چیز جالبی خواهید دید:
Enter your regex: a?
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a*
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a+
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
هر سه Quantifier حرف «a» را پیدا کردند، اما دو تای اول یک تطبیق با طول صفر در ایندکس ۱ نیز پیدا کردند؛ یعنی بعد از آخرین کاراکتر رشتهی ورودی. به یاد داشته باشید Matcher کاراکتر «a» را در سلول بین ایندکس ۰ و ۱ میبیند و ابزار测试 تا زمانی که بتواند تطبیقی پیدا کند حلقه میزند. بسته به Quantifier مورد استفاده، وجود «هیچچیز» در ایندکس بعد از آخرین کاراکتر ممکن است تطبیق را فعال کند یا نکند.
حالا رشتهی ورودی را به پنج حرف «a» پشت سر هم تغییر دهید و نتیجهی زیر را خواهید دید:
Enter your regex: a?
Enter input string to search: aaaaa
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 1 and ending at index 2.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "a" starting at index 3 and ending at index 4.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a*
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a+
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
عبارت a? برای هر کاراکتر یک تطبیق جداگانه پیدا میکند چون وقتی «a» صفر یا یک بار ظاهر شود تطبیق میدهد. عبارت a* دو تطبیق جداگانه پیدا میکند: تمام حروف «a» در تطبیق اول، سپس تطبیق با طول صفر بعد از آخرین کاراکتر در ایندکس ۵. و در نهایت a+ تمام مقادیر حرف «a» را تطبیق میدهد و وجود «هیچچیز» در ایندکس آخر را نادیده میگیرد.
در این مرحله ممکن است سؤال شود اگر دو Quantifier اول با حرفی غیر از «a» مواجه شوند چه میشود. مثلاً اگر حرف «b» را در «ababaaaab» ببینند چه؟
بیایید ببینیم:
Enter your regex: a?
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "a" starting at index 5 and ending at index 6.
I found the text "a" starting at index 6 and ending at index 7.
I found the text "a" starting at index 7 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a*
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a+
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
اگرچه حرف «b» در سلولهای ۱، ۳ و ۸ ظاهر میشود، خروجی تطبیق با طول صفر در آن موقعیتها گزارش میدهد. عبارت باقاعده a? بهطور خاص دنبال حرف «b» نیست؛ فقط به دنبال وجود (یا عدم وجود) حرف «a» است. اگر Quantifier تطبیق صفر بار «a» را مجاز بداند، هر چیزی در رشتهی ورودی که «a» نباشد بهصورت تطبیق با طول صفر ظاهر میشود.
برای تطبیق دقیقاً n بار، عدد را داخل آکولاد مشخص کنید:
Enter your regex: a{3}
Enter input string to search: aa
No match found.
Enter your regex: a{3}
Enter input string to search: aaa
I found the text "aaa" starting at index 0 and ending at index 3.
Enter your regex: a{3}
Enter input string to search: aaaa
I found the text "aaa" starting at index 0 and ending at index 3.
عبارت a{3} دنبال سه حرف «a» پشت سر هم میگردد. تست اول ناموفق است چون رشتهی ورودی به اندازهی کافی «a» ندارد. تست دوم دقیقاً ۳ «a» دارد که تطبیق را فعال میکند. تست سوم نیز تطبیق فعال میکند چون دقیقاً ۳ «a» در ابتدای رشته وجود دارد. هر چیزی بعد از آن برای تطبیق اول بیاهمیت است. اگر الگو دوباره ظاهر شود، تطبیقهای بعدی فعال میشوند:
Enter your regex: a{3}
Enter input string to search: aaaaaaaaa
I found the text "aaa" starting at index 0 and ending at index 3.
I found the text "aaa" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
برای نیاز به حداقل n بار، کاما بعد از عدد اضافه کنید:
Enter your regex: a{3,}
Enter input string to search: aaaaaaaaa
I found the text "aaaaaaaaa" starting at index 0 and ending at index 9.
با همان رشتهی ورودی، این تست فقط یک تطبیق پیدا میکند چون ۹ «a» پشت سر هم نیاز «حداقل ۳» «a» را برآورده میکند.
در نهایت، برای مشخص کردن سقف تعداد، عدد دوم را داخل آکولاد اضافه کنید:
Enter your regex: a{3,6} // find at least 3 (but no more than 6) a's in a row
Enter input string to search: aaaaaaaaa
I found the text "aaaaaa" starting at index 0 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
تطبیق اول مجبور است در سقف ۶ کاراکتر متوقف شود. تطبیق دوم شامل باقیمانده است که دقیقاً سه «a» است — حداقل تعداد مجاز برای این تطبیق. اگر رشتهی ورودی یک کاراکتر کوتاهتر بود، تطبیق دوم وجود نداشت چون فقط دو «a» باقی میماند.
تا الان فقط Quantifier ها را روی رشتههای ورودی حاوی یک کاراکتر آزمایش کردهایم. Quantifier ها فقط میتوانند به یک کاراکتر در هر لحظه متصل شوند، بنابراین عبارت باقاعده abc+ به معنای «a، به دنبال b، به دنبال c یک یا بیشتر بار» است. اما Quantifier ها همچنین میتوانند به کلاسهای کاراکتری و گروههای Capturing متصل شوند، مانند [abc]+ (a یا b یا c، یک یا بیشتر بار) یا (abc)+ (گروه «abc»، یک یا بیشتر بار).
با مشخص کردن گروه (dog) سه بار پشت سر هم نشان میدهیم.
Enter your regex: (dog){3}
Enter input string to search: dogdogdogdogdogdog
I found the text "dogdogdog" starting at index 0 and ending at index 9.
I found the text "dogdogdog" starting at index 9 and ending at index 18.
Enter your regex: dog{3}
Enter input string to search: dogdogdogdogdogdog
No match found.
در مثال اول سه تطبیق پیدا میشود چون Quantifier به کل گروه Capturing اعمال میشود. اگر پرانتزها را حذف کنید، تطبیق ناموفق میشود چون Quantifier {3} حالا فقط به حرف «g» اعمال میشود.
به همین ترتیب میتوانیم Quantifier را به یک کلاس کاراکتری کامل اعمال کنیم:
Enter your regex: [abc]{3}
Enter input string to search: abccabaaaccbbbc
I found the text "abc" starting at index 0 and ending at index 3.
I found the text "cab" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
I found the text "ccb" starting at index 9 and ending at index 12.
I found the text "bbc" starting at index 12 and ending at index 15.
Enter your regex: abc{3}
Enter input string to search: abccabaaaccbbbc
No match found.
Quantifier {3} در مثال اول به کل کلاس کاراکتری اعمال میشود اما در مثال دوم فقط به حرف «c».
تفاوتهای ظریفی بین Quantifier های Greedy (حریص)، Reluctant (بیمیل) و Possessive (مالکانه) وجود دارد.
Quantifier های Greedy به این دلیل «حریص» نامیده میشوند که Matcher را وادار میکنند کل رشتهی ورودی را قبل از اولین تلاش تطبیق بخواند (یا ببلعد). اگر اولین تلاش تطبیق (کل رشتهی ورودی) ناموفق باشد، Matcher یک کاراکتر از رشتهی ورودی عقب رفته و دوباره تلاش میکند. این فرآیند تا یافتن تطبیق یا تمام شدن کاراکترها تکرار میشود.
اما Quantifier های Reluctant رویکرد عکس دارند: از ابتدای رشتهی ورودی شروع کرده و به آرامی یک کاراکتر در هر لحظه میخورند تا تطبیقی پیدا کنند. آخرین چیزی که امتحان میکنند کل رشتهی ورودی است.
در نهایت، Quantifier های Possessive همیشه کل رشتهی ورودی را میبلعند و فقط یک بار (و فقط یک بار) برای تطبیق تلاش میکنند. برخلاف Quantifier های Greedy، Possessive ها هرگز عقب نمینشینند، حتی اگر عقبنشینی باعث موفقیت تطبیق شود.
برای نشان دادن تفاوت، رشتهی xfooxxxxxxfoo را در نظر بگیرید:
Enter your regex: .*foo // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.
Enter your regex: .*?foo // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.
Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.
مثال اول از Quantifier Greedy .* استفاده میکند. چون Quantifier حریص است، بخش .* ابتدا کل رشتهی ورودی را میبلعد. حالا عبارت کلی نمیتواند موفق شود چون سه حرف آخر («f» «o» «o») قبلاً مصرف شدهاند. بنابراین Matcher به آرامی یک حرف در هر لحظه عقب مینشیند تا راستترین وقوع «foo» بازگردانده شود و تطبیق موفق شود.
مثال دوم Reluctant است و با مصرف «هیچچیز» شروع میکند. چون «foo» در ابتدای رشته ظاهر نمیشود، مجبور است حرف اول (یک «x») را ببلعد که تطبیق اول را در ۰ و ۴ فعال میکند. ابزار测试 فرآیند را ادامه میدهد تا رشتهی ورودی تمام شود. تطبیق دیگری در ۴ و ۱۳ پیدا میکند.
مثال سوم تطبیقی پیدا نمیکند چون Quantifier Possessive است. در این حالت .*+ کل رشتهی ورودی را میبلعد و هیچچیزی برای «foo» در انتهای عبارت باقی نمیماند.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/