مقارنة السلاسل النصية مع تجاهل علامات التشكيل
تعلم كيفية مقارنة السلاسل النصية مع تجاهل العلامات التشكيلية باستخدام التطبيع في جافا سكريبت وIntl.Collator
مقدمة
عند بناء تطبيقات تعمل بلغات متعددة، غالبًا ما تحتاج إلى مقارنة السلاسل النصية التي تحتوي على علامات التشكيل. يجب أن يجد المستخدم الذي يبحث عن "cafe" نتائج لـ "café". يجب أن يتطابق فحص اسم المستخدم لـ "Jose" مع "José". تعامل المقارنة القياسية للسلاسل النصية هذه كسلاسل مختلفة، لكن منطق التطبيق الخاص بك يحتاج إلى معاملتها كمتساوية.
توفر جافا سكريبت نهجين لحل هذه المشكلة. يمكنك تطبيع السلاسل النصية وإزالة علامات التشكيل، أو استخدام واجهة برمجة التطبيقات المدمجة للترتيب لمقارنة السلاسل النصية باستخدام قواعد حساسية محددة.
ما هي علامات التشكيل
علامات التشكيل هي رموز توضع فوق أو تحت أو خلال الحروف لتعديل نطقها أو معناها. تسمى هذه العلامات بالحركات. تشمل الأمثلة الشائعة العلامة الحادة في "é"، والتلدة في "ñ"، والعلامتين النقطيتين في "ü".
في يونيكود، يمكن تمثيل هذه الأحرف بطريقتين. يمكن أن تمثل نقطة رمزية واحدة الحرف الكامل، أو يمكن أن تجمع نقاط رمزية متعددة بين حرف أساسي وعلامة تشكيل منفصلة. يمكن تخزين الحرف "é" كـ U+00E9 أو كـ "e" (U+0065) بالإضافة إلى علامة حادة مركبة (U+0301).
متى يتم تجاهل علامات التشكيل في المقارنات
وظيفة البحث هي حالة الاستخدام الأكثر شيوعًا للمقارنة غير الحساسة للتشكيل. يتوقع المستخدمون الذين يكتبون استعلامات بدون علامات تشكيل العثور على محتوى يحتوي على أحرف مشكلة. يجب أن يجد البحث عن "Muller" النتائج "Müller".
يتطلب التحقق من صحة إدخال المستخدم هذه القدرة عند التحقق مما إذا كانت أسماء المستخدمين أو عناوين البريد الإلكتروني أو المعرفات الأخرى موجودة بالفعل. تريد منع الحسابات المكررة لـ "maria" و "maría".
غالبًا ما تحتاج المقارنات غير الحساسة لحالة الأحرف إلى تجاهل علامات التشكيل في نفس الوقت. عند التحقق مما إذا كانت سلسلتان نصيتان متطابقتين بغض النظر عن الأحرف الكبيرة والصغيرة، فإنك عادة ما تريد تجاهل اختلافات التشكيل أيضًا.
إزالة علامات التشكيل باستخدام التطبيع
الطريقة الأولى تحول السلاسل النصية إلى شكل مطبّع حيث يتم فصل الحروف الأساسية وعلامات التشكيل، ثم تزيل علامات التشكيل.
التطبيع اليونيكود يحول السلاسل النصية إلى شكل قياسي. شكل NFD (التحليل القانوني) يفصل الأحرف المركبة إلى حروفها الأساسية وعلاماتها المركبة. السلسلة "café" تصبح "cafe" متبوعة بحرف تشكيل الشدة.
بعد التطبيع، يمكنك إزالة علامات التركيب باستخدام تعبير منتظم. نطاق اليونيكود من U+0300 إلى U+036F يحتوي على علامات التشكيل المركبة.
function removeAccents(str) {
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
const text1 = 'café';
const text2 = 'cafe';
const normalized1 = removeAccents(text1);
const normalized2 = removeAccents(text2);
console.log(normalized1 === normalized2); // true
console.log(normalized1); // "cafe"
تمنحك هذه الطريقة سلاسل نصية بدون علامات تشكيل يمكنك مقارنتها باستخدام عوامل المساواة القياسية.
يمكنك دمج هذا مع التحويل إلى أحرف صغيرة للمقارنات غير الحساسة لحالة الأحرف وعلامات التشكيل.
function normalizeForComparison(str) {
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
}
const search = 'muller';
const name = 'Müller';
console.log(normalizeForComparison(search) === normalizeForComparison(name)); // true
تعمل هذه الطريقة بشكل جيد عندما تحتاج إلى تخزين أو فهرسة النسخة المطبعة من السلاسل النصية للبحث الفعال.
مقارنة السلاسل النصية باستخدام Intl.Collator
الطريقة الثانية تستخدم واجهة برمجة التطبيقات Intl.Collator، التي توفر مقارنة للسلاسل النصية مع مراعاة اللغة ومستويات حساسية قابلة للتكوين.
كائن Intl.Collator يقارن السلاسل النصية وفقًا لقواعد لغة محددة. خيار الحساسية يتحكم في الاختلافات المهمة عند مقارنة السلاسل.
مستوى الحساسية "base" يتجاهل كلاً من علامات التشكيل واختلافات حالة الأحرف. السلاسل التي تختلف فقط في علامات التشكيل أو الأحرف الكبيرة تعتبر متساوية.
const collator = new Intl.Collator('en', { sensitivity: 'base' });
console.log(collator.compare('café', 'cafe')); // 0 (متساوية)
console.log(collator.compare('Café', 'cafe')); // 0 (متساوية)
console.log(collator.compare('café', 'caff')); // -1 (الأولى تأتي قبل الثانية)
طريقة compare تعيد 0 عندما تكون السلاسل متساوية، ورقمًا سالبًا عندما تأتي السلسلة الأولى قبل الثانية، ورقمًا موجبًا عندما تأتي السلسلة الأولى بعد الثانية.
يمكنك استخدام هذا للتحقق من المساواة أو لترتيب المصفوفات.
const collator = new Intl.Collator('en', { sensitivity: 'base' });
function areEqualIgnoringAccents(str1, str2) {
return collator.compare(str1, str2) === 0;
}
console.log(areEqualIgnoringAccents('José', 'Jose')); // true
console.log(areEqualIgnoringAccents('naïve', 'naive')); // true
للترتيب، يمكنك تمرير طريقة compare مباشرة إلى Array.sort.
const names = ['Müller', 'Martinez', 'Muller', 'Márquez'];
const collator = new Intl.Collator('en', { sensitivity: 'base' });
names.sort(collator.compare);
console.log(names); // يجمع المتغيرات معًا
توفر واجهة برمجة التطبيقات Intl.Collator مستويات حساسية أخرى لحالات استخدام مختلفة.
مستوى "accent" يتجاهل حالة الأحرف ولكنه يحترم اختلافات علامات التشكيل. "Café" تساوي "café" ولكن ليس "cafe".
const accentCollator = new Intl.Collator('en', { sensitivity: 'accent' });
console.log(accentCollator.compare('Café', 'café')); // 0 (متساوية)
console.log(accentCollator.compare('café', 'cafe')); // 1 (غير متساوية)
مستوى "case" يتجاهل علامات التشكيل ولكنه يحترم اختلافات حالة الأحرف. "café" تساوي "cafe" ولكن ليس "Café".
const caseCollator = new Intl.Collator('en', { sensitivity: 'case' });
console.log(caseCollator.compare('café', 'cafe')); // 0 (متساوية)
console.log(caseCollator.compare('café', 'Café')); // -1 (غير متساوية)
مستوى "variant" يحترم جميع الاختلافات. هذا هو السلوك الافتراضي.
const variantCollator = new Intl.Collator('en', { sensitivity: 'variant' });
console.log(variantCollator.compare('café', 'cafe')); // 1 (غير متساوية)
الاختيار بين التطبيع والترتيب
كلتا الطريقتين تنتج نتائج صحيحة للمقارنة غير الحساسة للتشكيل، لكنهما تتميزان بخصائص مختلفة.
طريقة التطبيع تنشئ سلاسل نصية جديدة بدون علامات التشكيل. استخدم هذا النهج عندما تحتاج إلى تخزين أو فهرسة النسخ المطبعة. محركات البحث وقواعد البيانات غالباً ما تخزن النص المطبع للبحث الفعال.
طريقة Intl.Collator تقارن السلاسل النصية دون تعديلها. استخدم هذا النهج عندما تحتاج إلى مقارنة السلاسل النصية مباشرة، مثل التحقق من التكرارات أو فرز القوائم. يحترم المرتب قواعد الفرز الخاصة باللغة التي لا تستطيع المقارنة البسيطة للسلاسل النصية التعامل معها.
اعتبارات الأداء تختلف حسب حالة الاستخدام. إنشاء كائن مرتب مرة واحدة وإعادة استخدامه يكون فعالاً للمقارنات المتعددة. تطبيع السلاسل النصية يكون فعالاً عندما تقوم بالتطبيع مرة واحدة وتقارن عدة مرات.
طريقة التطبيع تزيل معلومات التشكيل بشكل دائم. طريقة الترتيب تحافظ على السلاسل النصية الأصلية أثناء مقارنتها وفقاً للقواعد التي تحددها.
تصفية المصفوفات باستخدام البحث غير الحساس للتشكيل
حالة استخدام شائعة هي تصفية مصفوفة من العناصر بناءً على مدخلات المستخدم، مع تجاهل اختلافات التشكيل.
const products = [
{ name: 'Café Latte', price: 4.50 },
{ name: 'Crème Brûlée', price: 6.00 },
{ name: 'Croissant', price: 3.00 },
{ name: 'Café Mocha', price: 5.00 }
];
function searchProducts(query) {
const collator = new Intl.Collator('en', { sensitivity: 'base' });
return products.filter(product => {
return collator.compare(product.name.slice(0, query.length), query) === 0;
});
}
console.log(searchProducts('cafe'));
// Returns both Café Latte and Café Mocha
للمطابقة الفرعية، يعمل نهج التطبيع بشكل أفضل.
function removeAccents(str) {
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
function searchProducts(query) {
const normalizedQuery = removeAccents(query.toLowerCase());
return products.filter(product => {
const normalizedName = removeAccents(product.name.toLowerCase());
return normalizedName.includes(normalizedQuery);
});
}
console.log(searchProducts('creme'));
// Returns Crème Brûlée
يتحقق هذا النهج مما إذا كان اسم المنتج المطبع يحتوي على استعلام البحث المطبع كسلسلة فرعية.
التعامل مع تطابق إدخال النص
عند التحقق من صحة إدخال المستخدم مقابل البيانات الموجودة، تحتاج إلى مقارنة غير حساسة للعلامات التشكيلية لمنع الارتباك والتكرارات.
const existingUsernames = ['José', 'María', 'François'];
function isUsernameTaken(username) {
const collator = new Intl.Collator('en', { sensitivity: 'base' });
return existingUsernames.some(existing =>
collator.compare(existing, username) === 0
);
}
console.log(isUsernameTaken('jose')); // true
console.log(isUsernameTaken('Maria')); // true
console.log(isUsernameTaken('francois')); // true
console.log(isUsernameTaken('pierre')); // false
هذا يمنع المستخدمين من إنشاء حسابات بأسماء تختلف فقط في العلامات التشكيلية أو حالة الأحرف عن الحسابات الموجودة.
دعم المتصفح والبيئة
طريقة String.prototype.normalize مدعومة في جميع المتصفحات الحديثة وبيئات Node.js. متصفح Internet Explorer لا يدعم هذه الطريقة.
واجهة برمجة التطبيقات Intl.Collator مدعومة في جميع المتصفحات الحديثة وإصدارات Node.js. يتضمن Internet Explorer 11 دعمًا جزئيًا.
كلا النهجين يعملان بشكل موثوق في بيئات JavaScript الحالية. إذا كنت بحاجة إلى دعم متصفحات أقدم، فستحتاج إلى polyfills أو تنفيذات بديلة.
قيود إزالة العلامات التشكيلية
تستخدم بعض اللغات العلامات التشكيلية لإنشاء أحرف متميزة، وليس مجرد اختلافات في النطق. في اللغة التركية، "i" و "ı" هما حرفان مختلفان. في الألمانية، "ö" هو حرف علة متميز، وليس حرف "o" مع علامة تشكيلية.
إزالة العلامات التشكيلية تغير المعنى في هذه الحالات. ضع في اعتبارك ما إذا كانت المقارنة غير الحساسة للعلامات التشكيلية مناسبة لحالة الاستخدام واللغات المستهدفة لديك.
نهج التجميع يتعامل مع هذه الحالات بشكل أفضل لأنه يتبع قواعد خاصة باللغة المحلية. تحديد اللغة المحلية الصحيحة في منشئ Intl.Collator يضمن مقارنات مناسبة ثقافيًا.
const turkishCollator = new Intl.Collator('tr', { sensitivity: 'base' });
const germanCollator = new Intl.Collator('de', { sensitivity: 'base' });
ضع دائمًا في اعتبارك اللغات التي يدعمها تطبيقك عند اختيار استراتيجية المقارنة.