Почему я не могу использовать символы с диакритикой рядом с границей слова?

Я пытаюсь сделать динамический 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')

Предложения? Идеи?

8
задан tchrist 4 April 2015 в 19:04
поделиться

4 ответа

Реализация регулярного выражения JavaScript не поддерживает Юникод. Он знает только «символы слова» в стандартном младшем байтовом коде ASCII, который не включает é или любые другие буквы с диакритическими знаками или неанглийские буквы.

Поскольку é не является символом слова для JS, é , за которым следует пробел, никогда не может считаться границей слова. (Он соответствовал бы \ b , если бы использовался в середине слова, например Namés .)

/ ([\ s.,!?]) (Fancy namé | namé ) ([\ s.,!?] | $) /

Да, это был бы обычный обходной путь для JS (хотя, вероятно, с большим количеством знаков препинания). Для других языков вы обычно используете lookahead / lookbehind, чтобы избежать совпадения символов границы до и после, но они плохо поддерживаются / содержат ошибки в JS, поэтому их лучше избегать.

14
ответ дан 5 December 2019 в 06:37
поделиться

Возможно, попробуйте использовать флаги \ o или \ x при использовании вашего регулярного выражения.

Конец этого справочника по регулярным выражениям Javascript может вам помочь.

Что касается фактических восьмеричных / шестнадцатеричных значений é , я не уверен.

-1
ответ дан 5 December 2019 в 06:37
поделиться

Роб прав. Цитата из 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À-ÖØ-öø-ſ])/
7
ответ дан 5 December 2019 в 06:37
поделиться

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;
  }
});
1
ответ дан 5 December 2019 в 06:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: