٣١ مايو ٢٠٢٠

Lookahead و lookbehind

في بعض الأحيان نحتاج إلى العثور فقط على تلك المطابقات لنمط يتبعه أو يسبقه نمط آخر.

هناك صيغة خاصة لذلك ، تسمى “lookahead” و “lookbehind” ، يشار إليها معًا باسم “lookaround”.

في البداية ، دعنا نجد السعر من السلسلة مثل الموضوع: 1 ديك رومي يكلف 30 يورو. أي: رقم متبوعًا بعلامة .

Lookahead

الصيغة هي: X (؟ = Y) ، وتعني "ابحث عن X ، لكن تطابق فقط إذا تبعها "pattern: Y". قد يكون هناك أي نمط بدلاً منpattern:Xوpattern:Y`.

بالنسبة لرقم صحيح متبوعًا بـ, سيكون regexp \d+(?=€):

let str = "1 turkey costs 30€";

alert( str.match(/\d+(?=€)/) ); // 30, the number 1 is ignored, as it's not followed by €

يرجى ملاحظة: lookahead هو مجرد اختبار ، ومحتويات "نمط قوسين: (؟ = …)غير مدرجة في النتيجةmatch:30`.

عندما نبحث عن "pattern: X (؟ = Y)` ، يعثر محرك التعبير العادي على “pattern: X” ثم يتحقق مما إذا كان هناك “pattern: Y” بعده مباشرة. إذا لم يكن الأمر كذلك ، يتم تخطي المطابقة المحتملة ، ويستمر البحث.

من الممكن إجراء اختبارات أكثر تعقيدًا ، على سبيل المثال النمط: X (؟ = Y) (؟ = Z) يعني:

  1. ابحث عن النمط: X.
  2. تحقق مما إذا كان “النمط: Y” مباشرةً بعد “النمط: X” (يمكنك التخطي إذا لم يكن كذلك).
  3. تحقق مما إذا كان “النمط: Z” هو أيضًا مباشرةً بعد “النمط: X” (يمكنك التخطي إذا لم يكن كذلك).
  4. في حالة اجتياز كلا الاختبارين ، فإن “النمط: X” هو تطابق ، وإلا فتابع البحث.

بمعنى آخر ، يعني هذا النمط أننا نبحث عن X متبوعًا بـ Y و Z في نفس الوقت.

هذا ممكن فقط إذا كان النموذجان “pattern: Y” و “pattern: Z” لا يستبعد أحدهما الآخر.

على سبيل المثال ، \ d + (؟ = \ s) (؟ =. * 30) يبحث عن \ d + فقط إذا كان متبوعًا بمسافة ، ويوجد 30 في مكان ما بعده:

let str = "1 turkey costs 30€";

alert( str.match(/\d+(?=\s)(?=.*30)/) ); // 1

في سلسلتنا التي تتطابق تمامًا مع الرقم 1.

Negative lookahead

لنفترض أننا نريد كمية بدلاً من ذلك ، وليس سعرًا من نفس السلسلة. هذا رقم نمط: \ d + ، وليس متبوعًا بـ الموضوع: €.

لذلك ، يمكن تطبيق lookahead سلبي.

الصيغة هي: X (؟! Y) ، وتعني "search X ، ولكن فقط إذا لم يتبعها “pattern: Y`”.

let str = "2 turkeys cost 60€";

alert( str.match(/\d+(?!€)/) ); // 2 (the price is skipped)

Lookbehind

يسمح Lookahead بإضافة شرط لـ “ما يلي”.

Lookbehind مشابه ، لكنه يبدو في الخلف. أي أنه يسمح بمطابقة النمط فقط إذا كان هناك شيء قبله.

الصيغة هي:

  • نظرة إيجابية خلف: (؟ <= Y) X ، تطابق X ، ولكن فقط في حالة وجود "pattern: Y` قبلها.
  • مظهر سلبي خلف: (؟ <! Y) X ، يطابق X ، ولكن فقط في حالة عدم وجود Y قبله.

على سبيل المثال ، دعنا نغير السعر إلى الدولار الأمريكي. عادةً ما تكون علامة الدولار قبل الرقم ، لذلك للبحث عن $ 30 ، سنستخدمالنمط: (؟ <= \ $) \ d +- مبلغ يسبقهالموضوع: $`:

let str = "1 turkey costs $30";

// the dollar sign is escaped \$
alert( str.match(/(?<=\$)\d+/) ); // 30 (skipped the sole number)

وإذا احتجنا إلى الكمية – رقمًا ، لا يسبقه "الموضوع: $، فيمكننا استخدام النمط السلبي خلف ": (؟ <! \ $) \ d +:

let str = "2 turkeys cost $60";

alert( str.match(/(?<!\$)\d+/) ); // 2 (skipped the price)

التقاط المجموعات

بشكل عام ، لا تصبح المحتويات الموجودة داخل الأقواس حول جزء من النتيجة.

على سبيل المثال في النموذج \ d + (؟ = €) ، لا يتم التقاط علامة كجزء من المباراة. هذا طبيعي: نحن نبحث عن رقم نقش: \ d + ، بينما نقش: (؟ = €) هو مجرد اختبار يجب أن يتبعه الموضوع: €.

ولكن في بعض المواقف ، قد نرغب في التقاط تعبير lookaround أيضًا ، أو جزء منه. أن من الممكن. ما عليك سوى لف هذا الجزء بأقواس إضافية.

في المثال أدناه ، تم تسجيل “نمط علامة العملة: (€ | kr)” ، بالإضافة إلى المبلغ:

let str = "1 turkey costs 30€";
let regexp = /\d+(?=(€|kr))/; // extra parentheses around €|kr

alert( str.match(regexp) ); // 30, €

وإليك نفس الشيء بالنسبة إلى: lookbehind:

let str = "1 turkey costs $30";
let regexp = /(?<=(\$|£))\d+/;

alert( str.match(regexp) ); // 30, $

ملخص

Lookahead و lookbehind (يشار إليهما عادةً باسم “lookaround”) مفيدان عندما نرغب في مطابقة شيء ما اعتمادًا على السياق قبله / بعده.

بالنسبة إلى regexps البسيطة ، يمكننا القيام بنفس الشيء يدويًا. هذا هو: مطابقة كل شيء ، في أي سياق ، ثم التصفية حسب السياق في الحلقة.

تذكر أن str.match (بدون العلامة g) و str.matchAll (دائمًا) ترجع التطابقات كمصفوفات مع خاصيةindex ، حتى نعرف مكانها بالضبط في النص ، ويمكننا التحقق من سياق الكلام.

لكن بشكل عام يكون البحث أكثر ملاءمة.

أنواع Lookaround:

النمط النوع التطابق
X(?=Y) Positive lookahead X إذا تبعه Y
X(?!Y) Negative lookahead ``pattern: Xإذا لم يتبعه pattern: Y`
(?<=Y)X Positive lookbehind X إذا بعده Y
(?<!Y)X Negative lookbehind X إذا لم يكن بعده Y

مهمه

هناك سلسلة من الأعداد الصحيحة. أنشئ تعبيرًا عاديًا لا يبحث إلا عن الكلمات غير السلبية (يُسمح بصفر).

مثال للاستخدام:

let regexp = /your regexp/g;

let str = "0 12 -5 123 -18";

alert( str.match(regexp) ); // 0, 12, 123

regexp لرقم صحيح هو \ d +.

يمكننا استبعاد السلبيات عن طريق إلحاقها بالمظهر السلبي: (؟ <! -) \ d +.

على الرغم من أننا إذا جربناها الآن ، فقد نلاحظ نتيجة “إضافية” أخرى:

let regexp = /(?<!-)\d+/g;

let str = "0 12 -5 123 -18";

console.log( str.match(regexp) ); // 0, 12, 123, 8

كما ترون ، فإنه يطابق المباراة: 8 ، منالموضوع: -18. لاستبعاده ، نحتاج إلى التأكد من أن regexp يبدأ في مطابقة رقم ليس من منتصف رقم آخر (غير مطابق).

يمكننا القيام بذلك عن طريق تحديد مظهر سلبي آخر خلف: (؟ <! -) (؟ <! \ d) \ d +. الآن النمط: (؟ <! \ d) يضمن أن المطابقة لا تبدأ بعد رقم آخر ، فقط ما نحتاجه.

يمكننا أيضًا أن ننضم إليهم في lookbehind خلفنا هنا:

let regexp = /(?<![-\d])\d+/g;

let str = "0 12 -5 123 -18";

alert( str.match(regexp) ); // 0, 12, 123

لدينا سلسلة مع مستند HTML.

اكتب تعبيرًا عاديًا يُدرج <h1> مرحبًا </ h1> مباشرة بعد علامة <body>. قد يكون للسمات سمات.

على سبيل المثال:

let regexp = /your regular expression/;

let str = `
<html>
  <body style="height: 200px">
  ...
  </body>
</html>
`;

str = str.replace(regexp, `<h1>Hello</h1>`);

بعد هذا من المفترض أن تصبح قيمة str:

<html>
  <body style="height: 200px"><h1>Hello</h1>
  ...
  </body>
</html>

للإدراج بعد علامة <body> ، يجب أن نجدها أولاً. يمكننا استخدام نمط النقش العادي: <body. *> لذلك.

في هذه المهمة ، لا نحتاج إلى تعديل علامة <body>. نحتاج فقط لإضافة النص بعده.

إليك كيفية القيام بذلك:

let str = '...<body style="...">...';
str = str.replace(/<body.*>/, '$&<h1>Hello</h1>');

alert(str); // ...<body style="..."><h1>Hello</h1>...

في السلسلة البديلة $ & تعني المطابقة نفسها ، أي جزء النص المصدر الذي يتوافق مع <body. *>. يتم استبداله بمفرده بالإضافة إلى <h1> Hello </h1>.

البديل هو استخدام lookbehind:

let str = '...<body style="...">...';
str = str.replace(/(?<=<body.*>)/, `<h1>Hello</h1>`);

alert(str); // ...<body style="..."><h1>Hello</h1>...

كما ترون ، هناك فقط جزء وراء النظر في هذا التعبير العادي.

يعمل مثل هذا:

  • في كل موضع في النص.
  • تحقق مما إذا كان مسبوقًا بـ النمط: <body. *>.
  • إذا كان الأمر كذلك لدينا المباراة.

لن يتم إرجاع العلامة <body. *>. نتيجة هذا التعبير العادي هي حرفيا سلسلة فارغة ، لكنها تتطابق فقط في المواضع التي يسبقها النمط: <body. *>.

لذلك نستبدل “السطر الفارغ” ، مسبوقًا بـ "pattern: <body. *>، بـ

Hello

`. هذا هو الإدراج بعد “”.

ملاحظة. علامات Regexp ، مثل s و i يمكن أن تكون مفيدة أيضًا: / <body. *> / si. تجعل علامة s علامةpattern: .تتطابق مع حرف سطر جديد ، وعلامة 'pattern: i تجعلpattern: تتطابق أيضًا معmatch: ` غير حساس لحالة الأحرف.

خريطة الدورة التعليمية

التعليقات

إقرأ هذا قبل أن تضع تعليقًا…
  • إذا كان لديك اقتراحات أو تريد تحسينًا - من فضلك من فضلك إفتح موضوعًا فى جيتهاب أو شارك بنفسك بدلًا من التعليقات.
  • إذا لم تستطع أن تفهم شيئّا فى المقال - وضّح ماهو.
  • إذا كنت تريد عرض كود استخدم عنصر <code> ، وللكثير من السطور استخدم <pre>، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)