Я пытаюсь сделать динамический regex, который соответствует имени человека. Это работает без проблем над большинством имен, пока я не столкнулся с символами с диакритикой в конце имени.
Пример: некоторый необычный Namé
regex, который я использовал до сих пор:
/\b(Fancy Namé|Namé)\b/i
Используемый как это:
"Goal: Some Fancy Namé. Awesome.".replace(/\b(Fancy Namé|Namé)\b/i, '<a href="#">$1</a>');
Это просто не будет соответствовать. Если я заменяю é e, он соответствует очень хорошо. Если я пытаюсь соответствовать имени, такому как "Некоторый Необычный Naméa", он работает просто великолепно. Если я удаляю слово последняя граничная привязка к слову, оно работает просто великолепно.
Почему граница слова не отмечает работу здесь? Какие-либо предложения о том, как я обошел бы эту проблему?
Я рассмотрел использование чего-то вроде этого, но я не уверен, на что были бы похожи потери производительности:
"Some fancy namé. Allow me to ellaborate.".replace(/([\s.,!?])(fancy namé|namé)([\s.,!?]|$)/g, '$1<a href="#">$2</a>$3')
Предложения? Идеи?
Реализация регулярного выражения JavaScript не поддерживает Юникод. Он знает только «символы слова» в стандартном младшем байтовом коде ASCII, который не включает é
или любые другие буквы с диакритическими знаками или неанглийские буквы.
Поскольку é
не является символом слова для JS, é
, за которым следует пробел, никогда не может считаться границей слова. (Он соответствовал бы \ b
, если бы использовался в середине слова, например Namés
.)
/ ([\ s.,!?]) (Fancy namé | namé ) ([\ s.,!?] | $) /
Да, это был бы обычный обходной путь для JS (хотя, вероятно, с большим количеством знаков препинания). Для других языков вы обычно используете lookahead / lookbehind, чтобы избежать совпадения символов границы до и после, но они плохо поддерживаются / содержат ошибки в JS, поэтому их лучше избегать.
Возможно, попробуйте использовать флаги \ o
или \ x
при использовании вашего регулярного выражения.
Конец этого справочника по регулярным выражениям Javascript может вам помочь.
Что касается фактических восьмеричных / шестнадцатеричных значений é
, я не уверен.
Роб прав. Цитата из 3-го издания ECMAScript:
15.10.2.6 Утверждение:
Производственное Утверждение
\ b
оценивается как ...2. Вызов IsWordChar (e − 1) и пусть a будет логическим результатом
3. Вызов IsWordChar (e) и пусть b будет логическим результатом
и
Внутренняя вспомогательная функция IsWordChar ... выполняет следующее:
3. Если c - один из шестидесяти трех символов в таблице ниже, вернуть true .
abcdefghijklmnopqrstu vwxyz ABCDEFGHIJKLMNOPQRSTU VWXYZ 0 1 2 3 4 5 6 7 8 9 _
Поскольку é
не входит в число 63 символов, расположение между é
и a
будет считаться границей слова.
Если вам известен класс символов, вы можете использовать утверждение с отрицательным прогнозом, например
/(^|[^\wÀ-ÖØ-öø-ſ])(Fancy Namé|Namé)(?![\wÀ-ÖØ-öø-ſ])/
String.replace () принимает функцию обратного вызова в качестве второго параметра. (Не знаю, почему так много руководств по JS пропускают эту полезную функцию.) Таким образом, мы можем написать наш собственный тест для определения границ слов.
Решение, предложенное в другом месте, с регулярным выражением / (\ W | ^) (fancy namé | namé) (\ W | $) / ig
, дает ложные срабатывания в случае текста, такого как 'naméé' .
String.prototype.isWordCharAt = function(i) {
// should work for European languages and Unicode
return (this.charAt(i) >= 'A' && this.charAt(i) <= 'Z')
|| (this.charAt(i) >= 'a' && this.charAt(i) <= 'z')
|| (this.charCodeAt(i) >= 0xC0 && this.charCodeAt(i) < 0x2000)
;
};
"Namé. Goal: Some Fancy Namé. Namé. Nénamé. Namée. Nénamée. Namé"
.replace(/(Namé|Fancy Namé)/ig, function(
match, part1, /* part2, part3, ... */ offset, fullText) {
// Keep in mind that the number of arguments changes
// if the number of capturing parenthesis in regexp changes.
// We could use 'arguments' pseudo-array instead.
var len1 = part1.length;
var leftWordBoundary;
var rightWordBoundary;
if (offset === 0) {
leftWordBoundary = fullText.isWordCharAt(offset);
}
else {
leftWordBoundary = (fullText.isWordCharAt(offset - 1)
!= fullText.isWordCharAt(offset));
}
if (offset + len1 == fullText.length) {
rightWordBoundary = fullText.isWordCharAt(offset + len1 - 1);
}
else {
rightWordBoundary = (fullText.isWordCharAt(offset + len1 - 1)
!= fullText.isWordCharAt(offset + len1));
}
if (leftWordBoundary && rightWordBoundary) {
return '<a href="#">' + part1 + '</a>';
}
else {
return part1;
}
});