JavaRush /مدونة جافا /Random-AR /التعبيرات العادية في Java (RegEx)

التعبيرات العادية في Java (RegEx)

نشرت في المجموعة
التعبيرات العادية هي موضوع غالبًا ما يؤجله المبرمجون، حتى ذوي الخبرة، إلى وقت لاحق. ومع ذلك، سيتعين على معظم مطوري Java عاجلاً أم آجلاً التعامل مع معالجة النصوص. في أغلب الأحيان - مع عمليات البحث في النص والتحرير. بدون التعبيرات العادية، لا يمكن تصور رمز البرنامج الإنتاجي والمدمج المرتبط بمعالجة النص. لذا توقف عن تأجيل الأمر، ودعونا نتعامل مع "النظاميين" الآن. هذه ليست مهمة صعبة.

ما هو التعبير العادي RegEx؟

في الواقع، التعبير العادي (RegEx في Java) هو نمط للبحث عن سلسلة في النص. في Java، يكون التمثيل الأولي لهذا النمط دائمًا عبارة عن سلسلة، أي كائن من فئة السلسلة. ومع ذلك، لا يمكن تجميع أي سلسلة في تعبير عادي، فقط تلك التي تتبع قواعد كتابة التعبير العادي - بناء الجملة المحدد في مواصفات اللغة. لكتابة تعبير عادي، يتم استخدام الأحرف الأبجدية والرقمية، بالإضافة إلى الأحرف الأولية - وهي الأحرف التي لها معنى خاص في بناء جملة التعبيرات العادية. على سبيل المثال:
String regex = "java"; // string template "java";
String regex = "\\d{3}"; // string template of three numeric characters;

إنشاء التعبيرات العادية في جافا

لإنشاء RegEx في Java، عليك اتباع خطوتين بسيطتين:
  1. كتابتها كسلسلة باستخدام بناء جملة التعبير العادي؛
  2. تجميع هذه السلسلة في تعبير عادي؛
يبدأ العمل مع التعبيرات العادية في أي برنامج Java بإنشاء كائن فئة Pattern. للقيام بذلك، تحتاج إلى استدعاء إحدى الطريقتين الثابتتين المتوفرتين في الفصل compile. تأخذ الطريقة الأولى وسيطة واحدة - سلسلة حرفية للتعبير العادي، والثانية - بالإضافة إلى معلمة أخرى تقوم بتشغيل وضع مقارنة القالب بالنص:
public static Pattern compile (String literal)
public static Pattern compile (String literal, int flags)
يتم تعريف قائمة قيم المعلمات المحتملة flagsفي الفصل Patternوهي متاحة لنا كمتغيرات فئة ثابتة. على سبيل المثال:
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);//searching for matches with the pattern will be done case-insensitively.
في الأساس، الفصل Patternهو منشئ التعبير العادي. تحت الغطاء، compileتستدعي الطريقة المُنشئ الخاص للفئة Patternلإنشاء طريقة عرض مجمعة. يتم تنفيذ هذه الطريقة لإنشاء مثيل القالب بهدف إنشائه ككائن غير قابل للتغيير. عند الإنشاء، يتم إجراء فحص بناء الجملة للتعبير العادي. إذا كانت هناك أخطاء في السطر، فسيتم إنشاء استثناء PatternSyntaxException.

بناء جملة التعبير العادي

يعتمد بناء جملة التعبير العادي على استخدام الرموز <([{\^-=$!|]})?*+.>، والتي يمكن دمجها مع الأحرف الأبجدية. اعتمادًا على دورهم، يمكن تقسيمهم إلى عدة مجموعات:
1. الأحرف الأولية لمطابقة حدود الخط أو النص
حرف أولي غاية
^ بداية السطر
$ نهاية الخط
حدود الكلمة
ليس حدا للكلمة
بداية الإدخال
نهاية المباراة السابقة
نهاية الإدخال
نهاية الإدخال
2. الأحرف الأولية للبحث عن فئات الأحرف
حرف أولي غاية
رمز رقمي
حرف غير رقمي
حرف الفضاء
حرف غير مسافة بيضاء
حرف أبجدي رقمي أو الشرطة السفلية
أي حرف بخلاف الحروف الأبجدية أو الرقمية أو الشرطة السفلية
. أي شخصية
3. الأحرف الأولية للبحث عن رموز تحرير النص
حرف أولي غاية
\ ر حرف علامة التبويب
حرف السطر الجديد
\ ص حرف العودة
\F انتقل إلى الصفحة الجديدة
\u0085 حرف السطر التالي
\ش 2028 حرف فاصل الخط
\ش 2029 رمز فاصل الفقرة
4. الأحرف الأولية لتجميع الأحرف
حرف أولي غاية
[أ ب ج] أي مما سبق (أ، ب، ج)
[^اي بي سي] أي شيء آخر غير تلك المذكورة (ليس أ، ب، ج)
[أ-ي-ي] دمج النطاق (الأحرف اللاتينية من a إلى z غير حساسة لحالة الأحرف)
[إعلان[MP]] تسلسل الأحرف (من a إلى d ومن m إلى p)
[أ&&[تعريف]] تقاطع الرموز (الرموز d,e,f)
[من الألف إلى الياء &&[^قبل الميلاد]] طرح الأحرف (الأحرف a، dz)
5. Metasymbols للإشارة إلى عدد الأحرف - محددات الكمية. يأتي المحدد الكمي دائمًا بعد حرف أو مجموعة من الأحرف.
حرف أولي غاية
؟ واحد أو مفقود
* صفر مرة أو أكثر
+ مرة واحدة أو أكثر
{ن} ن مرات
{ن،} ن مرات أو أكثر
{ن،م} ما لا يقل عن n مرة ولا يزيد عن m مرة

وضع الكمي الجشع

الميزة الخاصة لمحددات الكمية هي القدرة على استخدامها في أوضاع مختلفة: الجشع، والجشع الشديد، والكسالى. يتم تشغيل الوضع الجشع الإضافي بإضافة الرمز " +" بعد محدد الكمية، والوضع البطيء بإضافة الرمز " ?". على سبيل المثال:
"A.+a" // greedy mode
"A.++a" // over-greedy mode
"A.+?a" // lazy mode
باستخدام هذا القالب كمثال، دعونا نحاول فهم كيفية عمل محددات الكمية في أوضاع مختلفة. بشكل افتراضي، يعمل محدد الكمية في الوضع الجشع. هذا يعني أنه يبحث عن أطول تطابق ممكن في السلسلة. نتيجة لتشغيل هذا الكود:
public static void main(String[] args) {
    String text = "Egor Alla Alexander";
    Pattern pattern = Pattern.compile("A.+a");
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(text.substring(matcher.start(), matcher.end()));
    }
}
سنحصل على المخرجات التالية: Alla Alexa . يتم تنفيذ خوارزمية البحث لنمط معين " А.+а" بالتسلسل التالي:
  1. في النمط المحدد، الحرف الأول هو الحرف الروسي А. Matcherيطابقها مع كل حرف في النص، بدءًا من الموضع صفر. يوجد رمز في الموضع صفر في نصنا Е، بحيث Matcherيمر عبر الأحرف الموجودة في النص بشكل تسلسلي حتى يتطابق مع النمط. في مثالنا، هذا هو الرمز الموجود في الموضع رقم 5.

    التعبيرات العادية في جافا - 2
  2. بعد العثور على تطابق مع الحرف الأول للنمط، Matcherفإنه يتحقق من التطابق مع الحرف الثاني من النمط. في حالتنا، هذا هو الرمز " ."، الذي يرمز إلى أي حرف.

    التعبيرات العادية في جافا - 3

    وفي الموضع السادس يوجد رمز الحرف л. وبطبيعة الحال، فإنه يطابق نمط "أي شخصية".

  3. Matcherينتقل إلى التحقق من الحرف التالي من النمط. في القالب الخاص بنا، يتم تحديده باستخدام .+محدد الكمية "". وبما أن عدد تكرارات "أي حرف" في النمط هو مرة واحدة أو أكثر، Matcherفإنه يأخذ الحرف التالي من السلسلة بدوره ويتحقق من مطابقته للنمط، طالما تم استيفاء شرط "أي حرف"، في مثالنا - حتى نهاية السطر ( من الموضع رقم 7 - رقم 18 من النص).

    التعبيرات العادية في جافا - 4

    في الواقع، Matcherفهو يلتقط السطر بأكمله حتى النهاية - وهنا يتجلى "جشعه".

  4. بعد Matcherالوصول إلى نهاية النص والانتهاء من التحقق من А.+الجزء " " من النمط، يبدأ Matcher في التحقق من بقية النمط - حرف الحرف а. بما أن النص في الاتجاه الأمامي قد انتهى، يتم التحقق في الاتجاه العكسي، بدءًا من الحرف الأخير:

    التعبيرات العادية في جافا - 5
  5. Matcher"يتذكر" عدد التكرارات في النمط " .+" الذي وصل عنده إلى نهاية النص، فيقوم بتقليل عدد التكرارات بمقدار واحد ويتحقق من نمط النص حتى يتم العثور على تطابق: التعبيرات العادية في جافا - 6

وضع الكمي الجشع للغاية

في وضع الجشع الفائق، يعمل المطابق بشكل مشابه لآلية وضع الجشع. والفرق هو أنه عندما تقوم بسحب النص إلى نهاية السطر، لا يوجد بحث عكسي. أي أن المراحل الثلاث الأولى في وضع الجشع الفائق ستكون مشابهة لوضع الجشع. بعد التقاط السلسلة بأكملها، يضيف المطابق بقية النمط ويقارنه بالسلسلة الملتقطة. في مثالنا، عند تنفيذ الطريقة الرئيسية بالنمط " А.++а"، لن يتم العثور على أي تطابقات. التعبيرات العادية في جافا - 7

وضع الكمي كسول

  1. في هذا الوضع، في المرحلة الأولية، كما هو الحال في الوضع الجشع، يتم البحث عن تطابق مع الحرف الأول من النمط:

    التعبيرات العادية في جافا - 8
  2. بعد ذلك، يبحث عن تطابق مع الحرف التالي في النمط - أي حرف:

    التعبيرات العادية في جافا - 9
  3. على عكس الوضع الجشع، يبحث الوضع البطيء عن أقصر تطابق في النص، لذلك بعد العثور على تطابق مع الحرف الثاني من النمط، والذي تم تحديده بنقطة ويطابق الحرف في الموضع رقم 6 من النص، فإنه Matcherسيتحقق مما إذا كان النص يطابق بقية النمط - الحرف " а" .

    التعبيرات العادية في جافا - 10
  4. بما أنه لم يتم العثور على تطابق مع النمط الموجود في النص (في الموضع رقم 7 في النص يوجد الرمز "" лMatcherفإنه يضيف "أي حرف" آخر في النمط، حيث يتم تحديده مرة واحدة أو أكثر، ومرة أخرى يقارن النمط مع النص في المواضع من رقم 5 إلى رقم 8:

    التعبيرات العادية في جافا - 11
  5. في حالتنا، تم العثور على تطابق، ولكن لم يتم الوصول إلى نهاية النص بعد. لذلك، من الموضع رقم 9، يبدأ الفحص بالبحث عن الحرف الأول للنمط باستخدام خوارزمية مشابهة ثم يكرر حتى نهاية النص.

    التعبيرات العادية في جافا - 12
نتيجة لهذه الطريقة، mainعند استخدام القالب " А.+?а"، سنحصل على النتيجة التالية: Alla Alexa كما يتبين من مثالنا، عند استخدام أوضاع قياس كمية مختلفة لنفس القالب، حصلنا على نتائج مختلفة. ولذلك فمن الضروري أخذ هذه الخاصية بعين الاعتبار واختيار الوضع المطلوب حسب النتيجة المطلوبة أثناء البحث.

الهروب من الأحرف في التعبيرات العادية

نظرًا لأن التعبير العادي في Java، أو بشكل أكثر دقة تمثيله الأولي، يتم تحديده باستخدام سلسلة حرفية، فمن الضروري مراعاة قواعد مواصفات Java التي تتعلق بالسلسلة الحرفية. على وجه الخصوص، يتم تفسير حرف الخط المائل العكسي " \" في سلسلة حرفية في التعليمات البرمجية المصدر لـ Java على أنه حرف هروب ينبه المترجم إلى أن الحرف الذي يتبعه هو حرف خاص ويجب تفسيره بطريقة خاصة. على سبيل المثال:
String s = "The root directory is \nWindows";//wrap Windows to a new line
String s = "The root directory is \u00A7Windows";//insert paragraph character before Windows
لذلك، في سلسلة حرفية تصف تعبيرًا عاديًا وتستخدم الحرف " \" (على سبيل المثال، للأحرف الأولية)، يجب مضاعفته حتى لا يفسره مترجم Java bytecode بشكل مختلف. على سبيل المثال:
String regex = "\\s"; // template for searching for space characters
String regex = "\"Windows\""; // pattern to search for the string "Windows"
يجب أيضًا استخدام حرف الشرطة المائلة العكسية المزدوجة للهروب من الأحرف الخاصة إذا كنا نخطط لاستخدامها كأحرف "عادية". على سبيل المثال:
String regex = "How\\?"; // template for searching the string "How?"

طرق فئة النمط

لدى الفئة Patternطرق أخرى للتعامل مع التعبيرات العادية: String pattern()– إرجاع تمثيل السلسلة الأصلية للتعبير العادي الذي تم إنشاء الكائن منه Pattern:
Pattern pattern = Pattern.compile("abc");
System.out.println(Pattern.pattern())//"abc"
static boolean matches(String regex, CharSequence input)- يسمح لك بالتحقق من التعبير العادي الذي تم تمريره في معلمة regex مقابل النص الذي تم تمريره في المعلمة input. العوائد: صحيح – إذا كان النص يطابق النمط؛ كاذبة – خلاف ذلك؛ مثال:
System.out.println(Pattern.matches("A.+a","Alla"));//true
System.out.println(Pattern.matches("A.+a","Egor Alla Alexander"));//false
int flags()- يُرجع flagsقيم معلمات القالب التي تم تعيينها عند إنشائه، أو 0 إذا لم يتم تعيين هذه المعلمة. مثال:
Pattern pattern = Pattern.compile("abc");
System.out.println(pattern.flags());// 0
Pattern pattern = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
System.out.println(pattern.flags());// 2
String[] split(CharSequence text, int limit)- يقسم النص الذي تم تمريره كمعلمة إلى مجموعة من العناصر String. تحدد المعلمة limitالحد الأقصى لعدد التطابقات التي يتم البحث عنها في النص:
  • متى limit>0limit-1يتم إجراء البحث عن التطابقات؛
  • في limit<0- يبحث عن جميع التطابقات في النص
  • عندما limit=0- يبحث عن جميع التطابقات في النص، بينما يتم تجاهل الأسطر الفارغة في نهاية المصفوفة؛
مثال:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("\\s");
    String[] strings = pattern.split(text,2);
    for (String s : strings) {
        System.out.println(s);
    }
    System.out.println("---------");
    String[] strings1 = pattern.split(text);
    for (String s : strings1) {
        System.out.println(s);
    }
}
إخراج وحدة التحكم: Egor Alla Anna -------- Egor Alla Anna سننظر في طريقة فئة أخرى لإنشاء كائن Matcherأدناه.

أساليب الطبقة المتطابقة

Matcherهي فئة يتم من خلالها إنشاء كائن للبحث عن الأنماط. Matcher- هذا "محرك بحث"، "محرك" للتعبيرات العادية. للبحث، يحتاج إلى إعطائه شيئين: نمط بحث و"عنوان" للبحث فيه. لإنشاء كائن، Matcherيتم توفير الطريقة التالية في الفصل Pattern: рublic Matcher matcher(CharSequence input) كوسيطة، تأخذ الطريقة سلسلة من الأحرف التي سيتم إجراء البحث فيها. هذه هي كائنات الفئات التي تنفذ الواجهة CharSequence. يمكنك تمرير ليس فقط String، ولكن أيضا StringBuffer، وكوسيطة . قالب البحث هو كائن الفئة الذي يتم استدعاء الطريقة عليه . مثال على إنشاء المطابق: StringBuilderSegmentCharBufferPatternmatcher
Pattern p = Pattern.compile("a*b");// compiled the regular expression into a view
Matcher m = p.matcher("aaaaab");//created a search engine in the text “aaaaab” using the pattern "a*b"
الآن، بمساعدة "محرك البحث" الخاص بنا، يمكننا البحث عن التطابقات، ومعرفة موضع التطابق في النص، واستبدال النص باستخدام أساليب الفصل. تبحث الطريقة boolean find()عن المطابقة التالية في النص مع النمط. باستخدام هذه الطريقة ومشغل الحلقة، يمكنك تحليل النص بأكمله وفقًا لنموذج الحدث (تنفيذ العمليات اللازمة عند وقوع حدث - العثور على تطابق في النص). على سبيل المثال، باستخدام طرق هذه الفئة، int start()يمكنك int end()تحديد مواضع التطابق في النص، وباستخدام الطرق String replaceFirst(String replacement)، String replaceAll(String replacement)يمكنك استبدال المطابقات في النص بنص بديل آخر. مثال:
public static void main(String[] args) {
    String text = "Egor Alla Anna";
    Pattern pattern = Pattern.compile("A.+?a");

    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        int start=matcher.start();
        int end=matcher.end();
        System.out.println("Match found" + text.substring(start,end) + " с "+ start + " By " + (end-1) + "position");
    }
    System.out.println(matcher.replaceFirst("Ira"));
    System.out.println(matcher.replaceAll("Olga"));
    System.out.println(text);
}
مخرجات البرنامج: تم العثور على تطابق Alla من 5 إلى 8 مواضع تم العثور على تطابق Anna من 10 إلى 13 موضعًا Egor Ira Anna Egor Olga Olga Egor Alla Anna من المثال، من الواضح أن الأساليب replaceFirstتنشئ replaceAllكائنًا جديدًا String- سلسلة، والتي هو النص المصدر الذي يتم فيه استبدال المطابقات مع القالب بالنص الذي تم تمريره إلى الطريقة كوسيطة. علاوة على ذلك، فإن الطريقة replaceFirstتحل محل المطابقة الأولى فقط، وجميع replaceAllالتطابقات في الاختبار. يبقى النص الأصلي دون تغيير. يمكن العثور على استخدام أساليب الصنف الأخرى Matcher، بالإضافة إلى أمثلة التعبيرات العادية، في هذه السلسلة من المقالات . العمليات الأكثر شيوعًا مع التعبيرات العادية عند العمل مع النص هي من الفئات Patternومضمنة Matcherفي ملف String. هذه طرق مثل split, matches, replaceFirst. replaceAllولكن في الواقع، "تحت غطاء محرك السيارة" يستخدمون Patternو Matcher. لذلك، إذا كنت بحاجة إلى استبدال نص أو مقارنة سلاسل في برنامج دون كتابة تعليمات برمجية غير ضرورية، فاستخدم أساليب String. إذا كنت بحاجة إلى إمكانات متقدمة، ففكر في الفئات Patternو Matcher.

خاتمة

يتم وصف التعبير العادي في برنامج Java باستخدام سلاسل تتطابق مع النمط المحدد بواسطة القواعد. عند تشغيل التعليمات البرمجية، تقوم Java بإعادة ترجمة هذه السلسلة إلى كائن فئة Patternوتستخدم كائن الفئة Matcherللعثور على التطابقات في النص. كما قلت في البداية، غالبًا ما يتم وضع التعبيرات النمطية جانبًا لوقت لاحق، حيث يعتبر هذا موضوعًا صعبًا. ومع ذلك، إذا فهمت أساسيات بناء الجملة، والأحرف الأولية، والهروب، ودراسة أمثلة التعبيرات العادية، فقد تبين أنها أبسط بكثير مما تبدو للوهلة الأولى.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION