قم بتبسيط مهام البرمجة الشائعة باستخدام Regex API
في الجزأين 1 و2 من هذه المقالة، تعرفت على التعبيرات العادية وRegex API. لقد تعرفت على الفصل الدراسيPattern
وتجولت في الأمثلة التي توضح بنيات التعبير العادي، بدءًا من مطابقة الأنماط البسيطة باستخدام السلاسل الحرفية وحتى المطابقة الأكثر تعقيدًا باستخدام النطاقات ومطابقات الحدود ومحددات الكمية. في هذا الجزء والأجزاء اللاحقة سننظر في القضايا التي لم يتم تناولها في الجزء الأول، وسندرس الأساليب المقابلة للفئات Pattern
، Matcher
و PatternSyntaxException
. ستتعلم أيضًا أداتين مساعدة تستخدمان التعبيرات العادية لتسهيل مشكلات البرمجة الشائعة. الأول يستخرج التعليقات من الكود للتوثيق. والثاني عبارة عن مكتبة من التعليمات البرمجية القابلة لإعادة الاستخدام والمصممة لإجراء تحليل معجمي - وهو مكون أساسي للمجمعات والمترجمين والبرامج المماثلة.
تنزيل كود المصدر
يمكنك الحصول على كافة التعليمات البرمجية المصدر (التي أنشأها Jeff Friesen لـ JavaWorld) للتطبيقات التجريبية في هذه المقالة من هنا .تعلم Regex API
Pattern
، Matcher
وهي PatternSyntaxException
الفئات الثلاث التي تشكل Regex API. يوفر كل واحد منهم طرقًا تسمح لك باستخدام التعبيرات العادية في التعليمات البرمجية الخاصة بك.
طرق فئة النمط
مثيل الفئة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
التي يمكن دمجها باستخدام bitwise OR (على سبيل المثال،CASE_INSENSITIVE | DOTALL
) وتمريرها كوسيطةflags
.
باستثناء
CANON_EQ, LITERAL и UNICODE_CHARACTER_CLASS
، تعد هذه الثوابت بديلاً لتعبيرات العلامات المتداخلة الموضحة في الجزء الأول. إذا تمت مصادفة ثابت إشارة غير تلك المحددة في الفئة 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 لمرة واحدة باستخدام القالب. تُرجع هذه الطريقة صحيحًا إذا input كان النمط مطابقًا regex ، وإلا فإنها تُرجع خطأ. إذا كان التعبير العادي يحتوي على خطأ في بناء الجملة، فستطرح الطريقة استثناءً PatternSyntaxException . على سبيل المثال، System.out.println(Pattern.matches("[a-z[\\s]]*", "all lowercase letters and whitespace only")); يطبع true ، مما يؤكد أن العبارة all lowercase letters and whitespace only تحتوي فقط على مسافات وأحرف صغيرة. |
تقسيم النص
قام معظم المطورين بكتابة تعليمات برمجية مرة واحدة على الأقل لتقسيم نص الإدخال إلى الأجزاء المكونة له، مثل تحويل حساب موظف قائم على النص إلى مجموعة من الحقول. يوفر الفصل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
مسندات القالب وواجهة برمجة تطبيقات 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 من الجزء الأول:
for (String progLang: progLangs)
if (p.matcher(progLang).find())
System.out.println(progLang);
أساليب الطبقة المتطابقة
يصف مثيل للفئةMatcher
آلية لتنفيذ عمليات مطابقة النمط على سلسلة من الأحرف عن طريق تفسير التعبير العادي المترجم للفئة Pattern
. تدعم كائنات الفئة Matcher
أنواعًا مختلفة من عمليات البحث عن الأنماط:
-
تبحث الطريقة
boolean find()
في نص الإدخال للمطابقة التالية. تبدأ هذه الطريقة في المسح إما في بداية النص المحدد أو عند الحرف الأول بعد المطابقة السابقة. يكون الخيار الثاني ممكنًا فقط إذا عاد الاستدعاء السابق لهذه الطريقة إلى "صحيح" ولم تتم إعادة تعيين المحلل. على أية حال، إذا نجح البحث، فسيتم إرجاع القيمة المنطقية الحقيقية. يمكن العثور على مثال لهذه الطريقة فيRegexDemo
الجزء الأول. -
تقوم الطريقة
boolean find(int start)
بإعادة تعيين المطابق والبحث في النص عن المطابقة التالية. يبدأ العرض من الموضع المحدد بواسطة المعلمةstart
. إذا نجح البحث، فسيتم إرجاع القيمة المنطقية الحقيقية. على سبيل المثال،m.find(1);
يقوم بمسح النص بدءًا من الموضع1
(يتم تجاهل الموضع 0). إذا كانت المعلمةstart
تحتوي على قيمة سالبة أو قيمة أكبر من طول النص المطابق، فستطرح الطريقة استثناءًjava.lang.IndexOutOfBoundsException
. -
تحاول الطريقة
boolean matches()
مطابقة كل النص بنمط ما. تقوم بإرجاع قيمة منطقية صحيحة إذا كان كل النص يطابق النمط.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.lang.StringBuffer
. تستخدم الطرق التالية هذا الموضع:
-
تقوم الطريقة
Matcher appendReplacement(StringBuffer sb, String replacement)
بقراءة أحرف النص المطابق وإلحاقها بنهاية الكائنStringBuffer
المشار إليه بواسطة الوسيطةsb
. تتوقف هذه الطريقة عن القراءة عند الحرف الأخير الذي يسبق مطابقة النمط السابق. بعد ذلك، تقوم الطريقة بإلحاق الأحرف من كائن النوعString
المشار إليه بواسطة الوسيطةreplacement
إلى نهاية الكائنStringBuffer
(قد تحتوي السلسلةreplacement
على إشارات إلى تسلسلات نصية تم التقاطها أثناء البحث السابق؛ ويتم تحديدها باستخدام الأحرف($)
وأرقام المجموعة التي يتم التقاطها). أخيرًا، تقوم الطريقة بتعيين قيمة موضع المطابق المراد إلحاقه بموضع آخر حرف مطابق بالإضافة إلى واحد، ثم تقوم بإرجاع مرجع إلى المطابق الحالي. -
تضيف الطريقة
StringBuffer appendTail(StringBuffer sb)
كل النص إلى كائنStringBuffer
وترجع مرجعًا لذلك الكائن. بعد استدعاء الأسلوب الأخيرappendReplacement(StringBuffer sb, String replacement)
، قم باستدعاء الأسلوبappendTail(StringBuffer sb)
لنسخ النص المتبقي إلى الكائنStringBuffer
.
تطرح الطريقة Matcher appendReplacement(StringBuffer sb, String replacement)
استثناءً java.lang.IllegalStateException
إذا لم يعثر المطابق بعد على تطابق أو فشلت محاولة بحث سابقة. إنه يطرح استثناءً IndexOutOfBoundsException
إذا كان السطر replacement
يحدد مجموعة التقاط غير موجودة في النمط).
المجموعات الملتقطة |
---|
كما تتذكر من الجزء الأول، مجموعة الالتقاط هي عبارة عن سلسلة من الأحرف المحاطة () بأحرف أولية بين قوسين ( ). الغرض من هذا البناء هو تخزين الأحرف التي تم العثور عليها لإعادة استخدامها لاحقًا أثناء مطابقة النمط. يتم اعتبار كافة الأحرف من المجموعة التي تم التقاطها ككل واحد أثناء البحث عن النمط. |
appendReplacement(StringBuffer sb, String replacement)
والطرق 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)
بإعادة تعيين المطابق، وإنشاء كائن جديدString
، ونسخ جميع أحرف نص المطابق (حتى المطابقة الأولى) إلى هذه السلسلة، وإلحاق الأحرف من نهايتها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(" "));
وهنا النتائج: Удаляем лишние пробелы.
التعبيرات العادية في جافا، الجزء 4 التعبيرات العادية في جافا، الجزء 5
GO TO FULL VERSION