اكتشف مطابقة الأنماط المتقدمة في JavaScript باستخدام التعبيرات النمطية. تعلم عن بناء جملة regex والتطبيقات العملية وتقنيات التحسين من أجل تعليمات برمجية فعالة وقوية.
مطابقة الأنماط في JavaScript باستخدام التعبيرات النمطية: دليل شامل
التعبيرات النمطية (regex) هي أداة قوية لمطابقة الأنماط ومعالجة النصوص في JavaScript. فهي تسمح للمطورين بالبحث عن السلاسل والتحقق منها وتحويلها بناءً على أنماط محددة. يوفر هذا الدليل نظرة عامة شاملة على التعبيرات النمطية في JavaScript، ويغطي بناء الجملة والاستخدام والتقنيات المتقدمة.
ما هي التعبيرات النمطية؟
التعبير النمطي هو سلسلة من الأحرف التي تحدد نمط بحث. تُستخدم هذه الأنماط لمطابقة السلاسل ومعالجتها. تُستخدم التعبيرات النمطية على نطاق واسع في البرمجة لمهام مثل:
- التحقق من صحة البيانات: التأكد من أن إدخال المستخدم يتوافق مع تنسيقات معينة (مثل عناوين البريد الإلكتروني وأرقام الهواتف).
- استخلاص البيانات: استرجاع معلومات معينة من النص (مثل استخلاص التواريخ أو عناوين URL أو الأسعار).
- البحث والاستبدال: العثور على النص واستبداله بناءً على أنماط معقدة.
- معالجة النصوص: تقسيم السلاسل أو ربطها أو تحويلها بناءً على قواعد محددة.
إنشاء تعبيرات نمطية في JavaScript
في JavaScript، يمكن إنشاء التعبيرات النمطية بطريقتين:
- باستخدام حرف التعبير النمطي: قم بتضمين النمط بين الشرطات المائلة (
/). - باستخدام مُنشئ
RegExp: قم بإنشاء كائنRegExpمع النمط كسلسلة.
مثال:
// باستخدام حرف التعبير النمطي
const regexLiteral = /hello/;
// باستخدام مُنشئ RegExp
const regexConstructor = new RegExp("hello");
يعتمد الاختيار بين الطريقتين على ما إذا كان النمط معروفًا في وقت الترجمة أو يتم إنشاؤه ديناميكيًا. استخدم تدوين الحرف عندما يكون النمط ثابتًا ومعروفًا مسبقًا. استخدم المُنشئ عندما يحتاج النمط إلى إنشائه برمجيًا، خاصةً عند دمج المتغيرات.
بناء جملة Regex الأساسية
تتكون التعبيرات النمطية من أحرف تمثل النمط المراد مطابقته. فيما يلي بعض مكونات regex الأساسية:
- الأحرف الحرفية: تطابق الأحرف نفسها (على سبيل المثال،
/a/تطابق الحرف 'a'). - الأحرف الوصفية: لها معانٍ خاصة (على سبيل المثال،
.،^،$،*،+،?،[]،{}،()،\،|). - فئات الأحرف: تمثل مجموعات من الأحرف (على سبيل المثال،
[abc]تطابق 'a' أو 'b' أو 'c'). - المحددات الكمية: تحدد عدد مرات ظهور حرف أو مجموعة (على سبيل المثال،
*،+،?،{n}،{n,}،{n,m}). - العناصر الثابتة: تطابق المواضع في السلسلة (على سبيل المثال،
^تطابق البداية،$تطابق النهاية).
الأحرف الوصفية الشائعة:
.(نقطة): تطابق أي حرف واحد باستثناء السطر الجديد.^(علامة الإقحام): تطابق بداية السلسلة.$(علامة الدولار): تطابق نهاية السلسلة.*(نجمة): تطابق صفرًا أو أكثر من تكرارات الحرف أو المجموعة السابقة.+(علامة الجمع): تطابق تكرارًا واحدًا أو أكثر للحرف أو المجموعة السابقة.?(علامة الاستفهام): تطابق صفرًا أو تكرارًا واحدًا للحرف أو المجموعة السابقة. تُستخدم للأحرف الاختيارية.[](الأقواس المربعة): تحدد فئة أحرف، تطابق أي حرف واحد داخل الأقواس.{}(الأقواس المتعرجة): تحدد عدد التكرارات التي يجب مطابقتها.{n}تطابق n مرات بالضبط،{n,}تطابق n مرات أو أكثر،{n,m}تطابق بين n و m مرات.()(الأقواس): تجمع الأحرف معًا وتلتقط السلسلة الفرعية المطابقة.\(شرطة مائلة عكسية): تفلت الأحرف الوصفية، مما يسمح لك بمطابقتها حرفيًا.|(خط الأنبوب): يعمل كعامل "أو"، يطابق التعبير قبله أو بعده.
فئات الأحرف:
[abc]: يطابق أي حرف من الأحرف a أو b أو c.[^abc]: يطابق أي حرف *ليس* a أو b أو c.[a-z]: يطابق أي حرف صغير من a إلى z.[A-Z]: يطابق أي حرف كبير من A إلى Z.[0-9]: يطابق أي رقم من 0 إلى 9.[a-zA-Z0-9]: يطابق أي حرف أبجدي رقمي.\d: يطابق أي رقم (يعادل[0-9]).\D: يطابق أي حرف غير رقمي (يعادل[^0-9]).\w: يطابق أي حرف كلمة (أبجدي رقمي بالإضافة إلى الشرطة السفلية؛ يعادل[a-zA-Z0-9_]).\W: يطابق أي حرف غير كلمة (يعادل[^a-zA-Z0-9_]).\s: يطابق أي حرف مسافة بيضاء (مسافة، علامة تبويب، سطر جديد، إلخ).\S: يطابق أي حرف غير مسافة بيضاء.
المحددات الكمية:
*: يطابق العنصر السابق صفر أو أكثر من المرات. على سبيل المثال،a*يطابق ""، "a"، "aa"، "aaa"، وما إلى ذلك.+: يطابق العنصر السابق مرة واحدة أو أكثر. على سبيل المثال،a+يطابق "a"، "aa"، "aaa"، ولكن ليس "".?: يطابق العنصر السابق صفر أو مرة واحدة. على سبيل المثال،a?يطابق "" أو "a".{n}: يطابق العنصر السابق بالضبط *n* مرات. على سبيل المثال،a{3}يطابق "aaa".{n,}: يطابق العنصر السابق *n* أو أكثر من المرات. على سبيل المثال،a{2,}يطابق "aa"، "aaa"، "aaaa"، وما إلى ذلك.{n,m}: يطابق العنصر السابق بين *n* و *m* مرات (شاملة). على سبيل المثال،a{2,4}يطابق "aa" أو "aaa" أو "aaaa".
العناصر الثابتة:
^: يطابق بداية السلسلة. على سبيل المثال،^Helloيطابق السلاسل التي *تبدأ* بـ "Hello".$: يطابق نهاية السلسلة. على سبيل المثال،World$يطابق السلاسل التي *تنتهي* بـ "World".\b: يطابق فاصل كلمة. هذا هو الموضع بين حرف كلمة (\w) وحرف غير كلمة (\W) أو بداية أو نهاية السلسلة. على سبيل المثال،\bword\bيطابق الكلمة الكاملة "word".
العلامات:
تقوم علامات Regex بتعديل سلوك التعبيرات النمطية. يتم إلحاقها بنهاية حرف regex أو تمريرها كوسيطة ثانية إلى مُنشئ RegExp.
g(عام): يطابق جميع تكرارات النمط، وليس فقط التكرار الأول.i(تجاهل حالة الأحرف): يُجري مطابقة غير حساسة لحالة الأحرف.m(متعدد الأسطر): يُمكّن وضع متعدد الأسطر، حيث تطابق^و$بداية ونهاية كل سطر (مفصول بـ\n).s(dotAll): يسمح للنقطة (.) بمطابقة أحرف السطر الجديد أيضًا.u(unicode): يُمكّن دعم Unicode الكامل.y(لاصق): يطابق فقط من الفهرس المشار إليه بواسطة خاصيةlastIndexلـ regex.
طرق Regex في JavaScript
يوفر JavaScript عدة طرق للعمل مع التعبيرات النمطية:
test(): يختبر ما إذا كانت سلسلة تطابق النمط. يُرجعtrueأوfalse.exec(): ينفذ بحثًا عن تطابق في سلسلة. يُرجع مصفوفة تحتوي على النص المطابق والمجموعات الملتقطة، أوnullإذا لم يتم العثور على أي تطابق.match(): يُرجع مصفوفة تحتوي على نتائج مطابقة سلسلة مقابل تعبير نمطي. يتصرف بشكل مختلف مع وبدون العلامةg.search(): يختبر وجود تطابق في سلسلة. يُرجع فهرس أول تطابق، أو -1 إذا لم يتم العثور على أي تطابق.replace(): يستبدل تكرارات النمط بسلسلة استبدال أو دالة تُرجع سلسلة الاستبدال.split(): يقسم سلسلة إلى مصفوفة من السلاسل الفرعية بناءً على تعبير نمطي.
أمثلة باستخدام طرق Regex:
// test()
const regex = /hello/;
const str = "hello world";
console.log(regex.test(str)); // Output: true
// exec()
const regex2 = /hello (\w+)/;
const str2 = "hello world";
const result = regex2.exec(str2);
console.log(result); // Output: ["hello world", "world", index: 0, input: "hello world", groups: undefined]
// match() with 'g' flag
const regex3 = /\d+/g; // Matches one or more digits globally
const str3 = "There are 123 apples and 456 oranges.";
const matches = str3.match(regex3);
console.log(matches); // Output: ["123", "456"]
// match() without 'g' flag
const regex4 = /\d+/;
const str4 = "There are 123 apples and 456 oranges.";
const match = str4.match(regex4);
console.log(match); // Output: ["123", index: 11, input: "There are 123 apples and 456 oranges.", groups: undefined]
// search()
const regex5 = /world/;
const str5 = "hello world";
console.log(str5.search(regex5)); // Output: 6
// replace()
const regex6 = /world/;
const str6 = "hello world";
const newStr = str6.replace(regex6, "JavaScript");
console.log(newStr); // Output: hello JavaScript
// replace() with a function
const regex7 = /(\d+)-(\d+)-(\d+)/;
const str7 = "Today's date is 2023-10-27";
const newStr2 = str7.replace(regex7, (match, year, month, day) => {
return `${day}/${month}/${year}`;
});
console.log(newStr2); // Output: Today's date is 27/10/2023
// split()
const regex8 = /, /;
const str8 = "apple, banana, cherry";
const arr = str8.split(regex8);
console.log(arr); // Output: ["apple", "banana", "cherry"]
تقنيات Regex المتقدمة
التقاط المجموعات:
تُستخدم الأقواس () لإنشاء مجموعات التقاط في التعبيرات النمطية. تسمح لك المجموعات الملتقطة باستخراج أجزاء معينة من النص المطابق. تُرجع طريقتي exec() و match() مصفوفة حيث يكون العنصر الأول هو التطابق بأكمله، والعناصر اللاحقة هي المجموعات الملتقطة.
const regex = /(\d{4})-(\d{2})-(\d{2})/
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match[0]); // Output: 2023-10-27 (The entire match)
console.log(match[1]); // Output: 2023 (The first captured group - year)
console.log(match[2]); // Output: 10 (The second captured group - month)
console.log(match[3]); // Output: 27 (The third captured group - day)
مجموعات التقاط مُسمّاة:
قدم ES2018 مجموعات التقاط مُسمّاة، والتي تسمح لك بتعيين أسماء لمجموعات التقاط باستخدام بناء الجملة (?<name>...). هذا يجعل التعليمات البرمجية أكثر قابلية للقراءة والصيانة.
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match.groups.year); // Output: 2023
console.log(match.groups.month); // Output: 10
console.log(match.groups.day); // Output: 27
مجموعات غير مُلتقِطة:
إذا كنت بحاجة إلى تجميع أجزاء من regex دون التقاطها (على سبيل المثال، لتطبيق محدد كمي على مجموعة)، فيمكنك استخدام مجموعة غير مُلتقِطة باستخدام بناء الجملة (?:...). يؤدي هذا إلى تجنب تخصيص الذاكرة غير الضرورية للمجموعات الملتقطة.
const regex = /(?:https?:\/\/)?([\w\.]+)/; // Matches a URL but only captures the domain name
const url = "https://www.example.com/path";
const match = regex.exec(url);
console.log(match[1]); // Output: www.example.com
نظرات حولية:
النظرات حولية هي تأكيدات ذات عرض صفر تطابق موضعًا في سلسلة بناءً على نمط يسبق (نظرة خلفية) أو يتبع (نظرة أمامية) هذا الموضع، دون تضمين نمط النظرة حولية في المطابقة نفسها.
- نظرة أمامية إيجابية:
(?=...)يطابق إذا كان النمط داخل النظرة الأمامية *يتبع* الموضع الحالي. - نظرة أمامية سلبية:
(?!...)يطابق إذا كان النمط داخل النظرة الأمامية *لا* يتبع الموضع الحالي. - نظرة خلفية إيجابية:
(?<=...)يطابق إذا كان النمط داخل النظرة الخلفية *يسبق* الموضع الحالي. - نظرة خلفية سلبية:
(?<!...)يطابق إذا كان النمط داخل النظرة الخلفية *لا* يسبق الموضع الحالي.
مثال:
// Positive Lookahead: Get the price only when followed by USD
const regex = /\d+(?= USD)/;
const text = "The price is 100 USD";
const match = text.match(regex);
console.log(match); // Output: ["100"]
// Negative Lookahead: Get the word only when not followed by a number
const regex2 = /\b\w+\b(?! \d)/;
const text2 = "apple 123 banana orange 456";
const matches = text2.match(regex2);
console.log(matches); // Output: null because match() only returns the first match without 'g' flag, which isn't what we need.
// to fix it:
const regex3 = /\b\w+\b(?! \d)/g;
const text3 = "apple 123 banana orange 456";
const matches3 = text3.match(regex3);
console.log(matches3); // Output: [ 'banana' ]
// Positive Lookbehind: Get the value only when preceded by $
const regex4 = /(?<=\$)\d+/;
const text4 = "The price is $200";
const match4 = text4.match(regex4);
console.log(match4); // Output: ["200"]
// Negative Lookbehind: Get the word only when not preceded by the word 'not'
const regex5 = /(?<!not )\w+/;
const text5 = "I am not happy, I am content.";
const match5 = text5.match(regex5); //returns first match if matched, not the array
console.log(match5); // Output: ['am', index: 2, input: 'I am not happy, I am content.', groups: undefined]
// to fix it, use g flag and exec(), but be careful since regex.exec saves the index
const regex6 = /(?<!not )\w+/g;
let text6 = "I am not happy, I am content.";
let match6; let matches6=[];
while ((match6 = regex6.exec(text6)) !== null) {
matches6.push(match6[0]);
}
console.log(matches6); // Output: [ 'I', 'am', 'happy', 'I', 'am', 'content' ]
المراجع الخلفية:
تسمح لك المراجع الخلفية بالإشارة إلى المجموعات الملتقطة مسبقًا ضمن نفس التعبير النمطي. يستخدمون بناء الجملة \1، \2، وما إلى ذلك، حيث يتوافق الرقم مع رقم المجموعة الملتقطة.
const regex = /([a-z]+) \1/;
const text = "hello hello world";
const match = regex.exec(text);
console.log(match); // Output: ["hello hello", "hello", index: 0, input: "hello hello world", groups: undefined]
التطبيقات العملية للتعبيرات النمطية
التحقق من صحة عناوين البريد الإلكتروني:
تتمثل حالة استخدام شائعة للتعبيرات النمطية في التحقق من صحة عناوين البريد الإلكتروني. بينما يكون regex للتحقق من صحة البريد الإلكتروني المثالي معقدًا للغاية، إليك مثال مبسط:
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
console.log(emailRegex.test("test@example.com")); // Output: true
console.log(emailRegex.test("invalid-email")); // Output: false
console.log(emailRegex.test("test@sub.example.co.uk")); // Output: true
استخلاص عناوين URL من النص:
يمكنك استخدام التعبيرات النمطية لاستخلاص عناوين URL من كتلة نصية:
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/g;
const text = "Visit our website at https://www.example.com or check out http://blog.example.org.";
const urls = text.match(urlRegex);
console.log(urls); // Output: ["https://www.example.com", "http://blog.example.org"]
تحليل بيانات CSV:
يمكن استخدام التعبيرات النمطية لتحليل بيانات CSV (قيم مفصولة بفواصل). إليك مثال على تقسيم سلسلة CSV إلى مصفوفة من القيم، والتعامل مع الحقول المقتبسة:
const csvString = 'John,Doe,"123, Main St",New York';
const csvRegex = /(?:"([^""]*(?:""[^""]*)*)")|([^,]+)/g; //Corrected CSV regex
let values = [];
let match;
while (match = csvRegex.exec(csvString)) {
values.push(match[1] ? match[1].replace(/""/g, '"') : match[2]);
}
console.log(values); // Output: ["John", "Doe", "123, Main St", "New York"]
التحقق من صحة رقم الهاتف الدولي
يعد التحقق من صحة أرقام الهواتف الدولية أمرًا معقدًا بسبب التنسيقات والأطوال المختلفة. غالبًا ما يتضمن الحل القوي استخدام مكتبة، ولكن يمكن لـ regex المبسط أن يوفر تحققًا أساسيًا من الصحة:
const phoneRegex = /^\+(?:[0-9] ?){6,14}[0-9]$/;
console.log(phoneRegex.test("+1 555 123 4567")); // Output: true (US Example)
console.log(phoneRegex.test("+44 20 7946 0500")); // Output: true (UK Example)
console.log(phoneRegex.test("+81 3 3224 5000")); // Output: true (Japan Example)
console.log(phoneRegex.test("123-456-7890")); // Output: false
التحقق من صحة قوة كلمة المرور
تُعد التعبيرات النمطية مفيدة لفرض سياسات قوة كلمة المرور. يتحقق المثال أدناه من الحد الأدنى للطول، والأحرف الكبيرة، والأحرف الصغيرة، ورقم.
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
console.log(passwordRegex.test("P@ssword123")); // Output: true
console.log(passwordRegex.test("password")); // Output: false (no uppercase or number)
console.log(passwordRegex.test("Password")); // Output: false (no number)
console.log(passwordRegex.test("Pass123")); // Output: false (no lowercase)
console.log(passwordRegex.test("P@ss1")); // Output: false (less than 8 characters)
تقنيات تحسين Regex
يمكن أن تكون التعبيرات النمطية مكلفة حسابيًا، خاصةً بالنسبة للأنماط المعقدة أو المدخلات الكبيرة. فيما يلي بعض التقنيات لتحسين أداء regex:
- كن محددًا: تجنب استخدام الأنماط العامة للغاية التي قد تطابق أكثر من المقصود.
- استخدم العناصر الثابتة: قم بتثبيت regex في بداية أو نهاية السلسلة كلما أمكن ذلك (
^،$). - تجنب التراجع: قلل من التراجع عن طريق استخدام المحددات الكمية التملكية (على سبيل المثال،
++بدلاً من+) أو المجموعات الذرية ((?>...)) عند الاقتضاء. - الترجمة مرة واحدة: إذا كنت تستخدم نفس regex عدة مرات، فقم بتجميعه مرة واحدة وأعد استخدام كائن
RegExp. - استخدم فئات الأحرف بحكمة: فئات الأحرف (
[]) أسرع بشكل عام من التبديلات (|). - حافظ عليها بسيطة: تجنب regexes المعقدة للغاية والتي يصعب فهمها وصيانتها. في بعض الأحيان، يمكن أن يكون تقسيم مهمة معقدة إلى تعبيرات regex متعددة وأبسط أو استخدام تقنيات أخرى لمعالجة السلاسل أكثر كفاءة.
أخطاء Regex الشائعة
- نسيان الهروب من الأحرف الوصفية: الفشل في الهروب من الأحرف الخاصة مثل
.،*،+،?،$،^،(،)،[،]،{،}،|، و\عندما تريد مطابقتها حرفيًا. - الإفراط في استخدام
.(النقطة): تطابق النقطة أي حرف (باستثناء السطر الجديد في بعض الأوضاع)، مما قد يؤدي إلى تطابقات غير متوقعة إذا لم يتم استخدامها بعناية. كن أكثر تحديدًا قدر الإمكان باستخدام فئات الأحرف أو الأنماط الأكثر تقييدًا. - الجشع: بشكل افتراضي، تكون المحددات الكمية مثل
*و+جشعة وستطابق قدر الإمكان. استخدم المحددات الكمية الكسولة (*؟،+؟) عندما تحتاج إلى مطابقة أقصر سلسلة ممكنة. - استخدام العناصر الثابتة بشكل غير صحيح: يمكن أن يؤدي سوء فهم سلوك
^(بداية السلسلة/السطر) و$(نهاية السلسلة/السطر) إلى مطابقة غير صحيحة. تذكر استخدام العلامةm(متعدد الأسطر) عند العمل مع سلاسل متعددة الأسطر وتريد أن تطابق^و$بداية ونهاية كل سطر. - عدم التعامل مع الحالات الهامشية: قد يؤدي الفشل في مراعاة جميع سيناريوهات الإدخال والحالات الهامشية المحتملة إلى حدوث أخطاء. اختبر تعبيرات regex الخاصة بك بدقة باستخدام مجموعة متنوعة من المدخلات، بما في ذلك السلاسل الفارغة والأحرف غير الصالحة وشروط الحدود.
- مشكلات الأداء: يمكن أن يؤدي إنشاء تعبيرات regex معقدة وغير فعالة للغاية إلى حدوث مشكلات في الأداء، خاصةً مع المدخلات الكبيرة. قم بتحسين تعبيرات regex الخاصة بك باستخدام أنماط أكثر تحديدًا، وتجنب التراجع غير الضروري، وتجميع تعبيرات regex التي يتم استخدامها بشكل متكرر.
- تجاهل ترميز الأحرف: قد يؤدي عدم معالجة ترميزات الأحرف بشكل صحيح (خاصةً Unicode) إلى نتائج غير متوقعة. استخدم العلامة
uعند العمل مع أحرف Unicode لضمان المطابقة الصحيحة.
الخلاصة
التعبيرات النمطية هي أداة قيمة لمطابقة الأنماط ومعالجة النصوص في JavaScript. يتيح لك إتقان بناء جملة وتقنيات regex حل مجموعة واسعة من المشكلات بكفاءة، بدءًا من التحقق من صحة البيانات وحتى معالجة النصوص المعقدة. من خلال فهم المفاهيم التي تمت مناقشتها في هذا الدليل والممارسة باستخدام أمثلة واقعية، يمكنك أن تتقن استخدام التعبيرات النمطية لتحسين مهاراتك في تطوير JavaScript.
تذكر أن التعبيرات النمطية يمكن أن تكون معقدة، وغالبًا ما يكون من المفيد اختبارها بدقة باستخدام مختبري regex عبر الإنترنت مثل regex101.com أو regexr.com. يسمح لك ذلك بتصور المطابقات وتصحيح أي مشكلات بفعالية. برمجة سعيدة!