JavaRush /مدونة جافا /Random-AR /التعبيرات العادية في جافا، الجزء 3

التعبيرات العادية في جافا، الجزء 3

نشرت في المجموعة
نقدم انتباهكم إلى ترجمة دليل قصير للتعبيرات العادية في Java، كتبه Jeff Friesen لموقع javaworld . ولتسهيل القراءة قمنا بتقسيم المقال إلى عدة أجزاء. التعبيرات العادية في جافا، الجزء 3 - 1التعبيرات العادية في جافا، الجزء 1 التعبيرات العادية في جافا، الجزء 2

قم بتبسيط مهام البرمجة الشائعة باستخدام 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تحتوي فقط على مسافات وأحرف صغيرة.
التعبيرات العادية في جافا، الجزء 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

مسندات القالب وواجهة برمجة تطبيقات 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".

التعبيرات العادية في جافا، الجزء 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.

المجموعات الملتقطة
كما تتذكر من الجزء الأول، مجموعة الالتقاط هي عبارة عن سلسلة من الأحرف المحاطة ()بأحرف أولية بين قوسين ( ). الغرض من هذا البناء هو تخزين الأحرف التي تم العثور عليها لإعادة استخدامها لاحقًا أثناء مطابقة النمط. يتم اعتبار كافة الأحرف من المجموعة التي تم التقاطها ككل واحد أثناء البحث عن النمط.
تستدعي التعليمة البرمجية التالية الأساليب 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
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION