اذهب إلى المحتوى

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

التابع (str.match(regexp

يبحث هذا التابع عن تطابقات للتعبير regexp في النص str، وله ثلاثة أنماط:

النمط الأول، الراية g غير مفعّلة: يعيد التابع التطابق الأول ضمن مصفوفة، تحوي مجموعات ملتقطةً capturing groups وخصائص، هي موقع التطابق index، والنص الذي نبحث فيه input، وهو النص str.

let str = "I love JavaScript";

let result = str.match(/Java(Script)/);

alert( result[0] );     // JavaScript (تطابق كامل)
alert( result[1] );     // Script (المجموعة الملتقطة الأولى)
alert( result.length ); // 2

// Additional information:
alert( result.index );  // 7 (موقع التطابق)
alert( result.input );  // I love JavaScript (النص الأصلي)

النمط الثاني، الراية g مفعلة: سيعيد التابع مصفوفةً تضم كل التطابقات الموجودة في صيغة قيم نصية، دون مجموعات ملتقطة، أو غيرها من التفاصيل.

let str = "I love JavaScript";

let result = str.match(/Java(Script)/g);

alert( result[0] ); // JavaScript
alert( result.length ); // 1

النمط الثالث، إذا لم يوجد تطابق فسيعيد التابع القيمة null، سواء استخدمنا الراية g أم لم نستخدمها، انتبه جيدًا إلى أنه لا يعيد مصفوفةً فارغةً عندما لا يجد تطابقات، بل يعيد القيمة null:

let str = "I love JavaScript";

let result = str.match(/HTML/);

alert(result); // null
alert(result.length); // Error

إذا أردنا الحصول على النتيجة في مصفوفة، فيمكننا كتابة الشيفرة على الشكل:

let result = str.match(regexp) || [];

التابع (str.matchAll(regexp

اقتباس

إضافة جديدة إلى اللغة، وقد تحتاج المتصفحات القديمة إلى شيفرات موائمة polyfills لتعويض نقص الدعم فيها.

يمثل التابع نسخةً محدثةً ومطورةً عن التابع str.match، ويستخدَم لإيجاد جميع التطابقات وفق المجموعات المحددة، ويختلف عن التابع str.match في ثلاثة أمور، هي:

  1. لا يعيد مصفوفةً بل كائنًا قابلًا للتكرار iterable object، ويمكن إنشاء مصفوفة نظامية منه باستخدام Array.from.
  2. عند استخدام الراية g يعيد كل تطابق في مصفوفة تحتوي مجموعات.
  3. عندما لا يجد تطابقات فلا يعيد null، بل كائنًا فارغًا قابلًا للتكرار.

أمثلة عن استخدامه:

let str = '<h1>Hello, world!</h1>';
let regexp = /<(.*?)>/g;

let matchAll = str.matchAll(regexp);

alert(matchAll); // ليس مصفوفة بل كائن

matchAll = Array.from(matchAll); // الآن مصفوفة

let firstMatch = matchAll[0];
alert( firstMatch[0] );  // <h1>
alert( firstMatch[1] );  // h1
alert( firstMatch.index );  // 0
alert( firstMatch.input );  // <h1>Hello, world!</h1>

إذا استخدمنا الحلقة for..of للحصول على تطابقات matchAll، فلن نحتاج إلى تحويل الكائن إلى مصفوفة من خلال Array.from.

التابع (str.split(regexp|substr, limit

يقسم النص وفقًا لتعبير نمطي (أو نص فرعي)، ويمكن استخدامه دون نص بالشكل التالي:

alert('12-34-56'.split('-')) // array of ['12', '34', '56']

كما يمكن التقسيم وفقًا لتعبير نمطي بنفس الأسلوب:

alert('12, 34, 56'.split(/,\s*/)) // array of ['12', '34', '56']

التابع (str.search(regexp

يعيد التابع موقع التطابق الأول، أو يعيد 1- إذا لم يجد تطابقًا:

let str = "A drop of ink may make a million think";

alert( str.search( /ink/i ) ); // 10 (first match position)

المحدودية الأكبر للتابع هي إيجاده لأول تطابق فقط. فإذا أردنا مواقع بقية التطابقات فلا بدّ من استخدام طرق أخرى، مثل البحث عن جميع التطابقات باستخدام matchAll.

التابع (str.replace(str|regexp, str|func

وهو التابع الأساسي للبحث والاستبدال، والأكثر فائدةً، ويمكن استخدامه للبحث عن أجزاء من النص دون الحاجة لتعابير نمطية.

// بدل الشرطة القصيرة بنقطتين متعامدين
alert('12-34-56'.replace("-", ":")) // 12:34-56

مع ذلك قد يصعب استخدامه أحيانًا.

عندما يكون المعامل الأول replace نصًا فسيستبدل التطابق الأول فقط حيث ستلاحظ في المثال الأول استبدال الشرطة القصيرة الأولى فقط بالنقطتين المتعامدتين، ولإيجاد بقية التطابقات واستبدالها، لا بدّ من استخدام التعبير النمطي g/-/ بدلًا من النص "-"، مع التفعيل الإجباري للراية g.

// بدل كل شرطة قصيرة بنقطتين متعامدين
alert( '12-34-56'.replace( /-/g, ":" ) )  // 12:34:56

يمكن استخدام محارف خاصة في الوسيط الثاني كونه النص البديل.

الرمز ما يفعله ضمن النص replacment
&$ يمثل نص التطابق
`$ يمثل النص الواقع قبل نص التطابق.
'$ يمثل النص الواقع بعد نص التطابق.
n$ يمثل التطابق ذا الرقم n من مجموعة التطابق (الموجود ضمن قوسي تجميع"()" ) وسنتعرف لاحقًا عليها في فصل: المجموعات الملتقطة.
‎$<name>‎ يمثل التطابق ذا الاسم name من مجموعة التطابق (الموجود ضمن قوسي تجميع"()" )، وسنتعرف لاحقًا ععليها في فصل: المجموعات الملتقطة.
$$ يمثل المحرف $

إليك مثالًا:

let str = "John Smith";

// swap first and last name
alert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John

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

func(match, p1, p2, ..., pn, offset, input, groups)

حيث:

  1. match: التطابق.
  2. p1, p2, ..., pn: محتويات المجموعات الملتقطة، إن وجدت.
  3. offset: موقع التطابق.
  4. input: النص الأصلي.
  5. groups: كائن يضم المجموعات المُسمّاة.

إذا لم توجد أقواس ضمن التعبير النمطي فسيكون لدينا ثلاثة وسطاء فقط (func(str, offset, input.

وسنعرض بعض الأمثلة:

  • تحويل التطابقات إلى أحرف كبيرة:
let str = "html and css";

let result = str.replace(/html|css/gi, str => str.toUpperCase());

alert(result); // HTML and CSS
  • استبدال كل تطابق بموقعه في النص:
alert("Ho-Ho-ho".replace(/ho/gi, (match, offset) => offset)); // 0-3-6
  • في المثال التالي، ستجد قوسين مفتوحين في التعبير النمطي وبالتالي ستقبل الدالة خمسة وسطاء، الأول للتطابق بأكمله، ثم محتوى القوسين، وبعدهما (لم يستخدما في مثالنا) موقع التطابق والنص الأصلي:
let str = "John Smith";

let result = str.replace(/(\w+) (\w+)/, (match, name, surname) => `${surname}, ${name}`);

alert(result); // Smith, John

يفضل استخدام التفكيك destruction عند وجود مجموعات عدة:

let str = "John Smith";

let result = str.replace(/(\w+) (\w+)/, (...match) => `${match[2]}, ${match[1]}`);

alert(result); // Smith, John

عندما نستخدم المجموعات المسماة، فسيكون الكائن groups مع المجموعات دائمًا في الموقع الأخير، وبالتالي سنحصل عليها بالشكل التالي:

let str = "John Smith";

let result = str.replace(/(?<name>\w+) (?<surname>\w+)/, (...match) => {
  let groups = match.pop();

  return `${groups.surname}, ${groups.name}`;
});

alert(result); // Smith, John

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

التابع (str.replaceAll(str|regexp, str|func

وله وظيفة التابع str.replace نفسها، مع وجود اختلافين رئيسيين:

  1. سيستبدل كل التطابقات الموجودة إذا كان وسيطه الأول نصًا، بينما يستبدل replace التطابق الأول فقط.
  2. يعمل تمامًا مثل التابع replace إذا كان وسيطه الأول تعبيرًا نمطيًا والراية g مفعلةً، ويعطي خطأً إذا لم تكن كذلك.

ويستخدم بشكل رئيسي عندما نريد استبدال جميع التطابقات، وإليك مثالًا:

// استبدل كل الشرطات بنقطتين عموديتين
alert('12-34-56'.replaceAll("-", ":")) // 12:34:56

التابع (regexp.exec(str

يعيد هذا التابع تطابقًا مع نمط إذا وجده ضمن النص، وعلى خلاف التوابع السابقة سيُستدعى من قبل كائن تعبير نمطي regexp وليس من قبل نص str، ويسلك سلوكًا مختلفًا عند تفعيل الراية g أو عدم تفعيلها، فإذا لم تكن هذه الراية مفعلةً فسيعيد التطابق الأول فقط، تمامًا مثل التابع (str.match(regexp، ولن يقدم هذا السلوك أي جديد، ولكن مع وجود الراية g:

  • سيعيد التطابق الأول، ويخزن الموقع الذي يلي التطابق مباشرةً ضمن الخاصية regexp.lastIndex.
  • عندما يُستدعى مجددًا سيبدأ البحث انطلاقًا من الموقع المُخزّّن ضمن الخاصية regexp.lastIndex معيدًا التطابق التالي، وسيخزن الموقع الذي يليه مباشرةً ضمن الخاصية regexp.lastIndex محدّثًا قيمتها.
  • وهكذا يستمر العمل.
  • إذا لم يجد تطابقات فسيعيد القيمة null، ويسند القيمة 0 إلى الخاصية regexp.lastIndex.

سيعيد الاستدعاء المتكرر لهذا التابع كل التطابقات كما شرحنا في الخطوات السابقة، بينما قبل وجود هذا التابع، كان لا بدّ من استخدام الحلقات للحصول على التطابقات جميعها:

let str = 'More about JavaScript at https://javascript.info';
let regexp = /javascript/ig;

let result;

while (result = regexp.exec(str)) {
  alert( `Found ${result[0]} at position ${result.index}` );
  // Found JavaScript at position 11, then
  // Found javascript at position 33
}

سيعمل الأسلوب المتبع في المثال السابق أيضًا، على الرغم من أنّ استخدام التابع str.matchAll سيناسب المتصفحات الحديثة أكثر.

يمكن استخدام التابع (regexp.exec(str للبحث انطلاقًا من موقع محدد بضبط قيمة الخاصية regexp.lastIndex يدويًا. وإليك مثالًا:

let str = 'Hello, world!';

let regexp = /\w+/g; //lastIndex يتجاهل المحرك قيمة الخاصية "g" دون الراية 
regexp.lastIndex = 5; // البحث انطلاقًا من الموقع 5

alert( regexp.exec(str) ); // world

يفرض وجود الراية y البحث في الموقع المحدد ضمن الخاصية regexp.lastIndex تمامًا، وليس بعده.

لنستبدل الراية y بالراية g في المثال السابق، وسنلاحظ عدم وجود تطابقات:

let str = 'Hello, world!';

let regexp = /\w+/y;
regexp.lastIndex = 5; // search exactly at position 5

alert( regexp.exec(str) ); // null

يناسب هذا الأمر الحالات التي نحتاج فيها إلى قراءة شيء ما من نص باستخدام التعابير النمطية انطلاقًا من موقع محدد تمامًا.

التابع (regexp.test(str

يتأكد هذا التابع من وجود تطابق، ويعيد إحدى القيمتين true/false، وإليك مثالًا:

let str = "I love JavaScript";

// ينفذ الاختباران التاليان العمل نفسه
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true

مثال مع جواب سلبي:

let str = "Bla-bla-bla";

alert( /love/i.test(str) ); // false
alert( str.search(/love/i) != -1 ); // false

في الحالة التي نفعل فيها الراية g، سيبحث التابع عن الخاصية regexp.lastIndex ويحدّث قيمتها، تمامًا مثل التابع regexp.exec، لذلك يمكن استخدامه للبحث في موقع محدد:

let regexp = /love/gi;

let str = "I love JavaScript";

// يبدأ البحث من الموقع 10
regexp.lastIndex = 10;
alert( regexp.test(str) ); // false (لا تطابق)

لاحظ أنه قد يخفق الاختبار المستمر لتعبير نمطي عام على نصوص مختلفة، لأن التابع regexp.exec يستدعي قيمًا متقدمةً للخاصية regexp.lastIndex، وبالتالي قد يبدأ البحث في نص آخر ابتداءً من موقع مختلف عن الصفر.

لاحظ في هذا المثال كيف سنختبر النص ذاته مرتين متتاليتين، وسيخفق الاختبار الثاني:

let regexp = /javascript/g;  // (regexp just created: regexp.lastIndex=0)

alert( regexp.test("javascript") ); // true (regexp.lastIndex=10 now)
alert( regexp.test("javascript") ); // false

للالتفاف على هذه المشكلة، يمكننا ضبط قيمة الخاصية regexp.lastIndexعلى الصفر قبل البدء بكل بحث، أو استخدام توابع النصوص، مثل .../str.match/search، بدلًا من توابع التعابير النمطية، فهي لا تستخدم الخاصية lastIndex.

ترجمة -وبتصرف- للفصل Methods of RegExp and string من سلسلة The Modern JavaScript Tutorial.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...