10

我正在尝试制作一个与人名匹配的动态正则表达式。它对大多数名称都没有问题,直到我在名称末尾遇到重音字符。

示例:一些花哨的名字

到目前为止我使用的正则表达式是:

/\b(Fancy Namé|Namé)\b/i

像这样使用:

"Goal: Some Fancy Namé. Awesome.".replace(/\b(Fancy Namé|Namé)\b/i, '<a href="#">$1</a>');

这根本不匹配。如果我将 é 替换为 ae,则匹配得很好。如果我尝试匹配诸如“Some Fancy Namea”之类的名称,它就可以正常工作。如果我删除单词最后一个单词边界锚,它工作得很好。

为什么单词边界标志在这里不起作用?关于如何解决这个问题的任何建议?

我考虑过使用这样的东西,但我不确定性能损失会是什么样子:

"Some fancy namé. Allow me to ellaborate.".replace(/([\s.,!?])(fancy namé|namé)([\s.,!?]|$)/g, '$1<a href="#">$2</a>$3')

建议?想法?

4

7 回答 7

20

JavaScript 的正则表达式实现不支持 Unicode。它只知道标准低字节 ASCII 中的“单词字符”,不包括é或任何其他重音或非英文字母。

因为é对JS来说不是单词字符,é后面跟一个空格绝对不能算单词边界。\b(如果在单词中间使用它会匹配,例如Namés.)

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

是的,这将是 JS 通常的解决方法(尽管可能有更多的标点符号)。对于其他语言,您通常会使用前瞻/后视来避免匹配前后边界字符,但这些在 JS 中的支持很差/有缺陷,因此最好避免。

于 2010-03-15T19:30:47.037 回答
8

罗伯是对的。引自 ECMAScript 第 3 版:

15.10.2.6 断言:

生产断言 \b评估...

2.调用IsWordChar(e−1)并设a为布尔结果
3.调用IsWordChar(e)并设b为布尔结果

内部辅助函数IsWordChar ... 执行以下操作:

3.如果c是下表中的 63 个字符之一,则返回true

a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9 _

由于é不是这 63 个字符之一,因此 和 之间的位置éa被视为单词边界。

如果您知道字符的类别,则可以使用否定的前瞻性断言,例如

/(^|[^\wÀ-ÖØ-öø-ſ])(Fancy Namé|Namé)(?![\wÀ-ÖØ-öø-ſ])/
于 2010-03-15T19:32:59.323 回答
4
于 2010-11-18T17:43:51.573 回答
2

String.replace() 接受回调函数作为其第二个参数。(不知道为什么这么多 JS 教程忽略了这个有用的功能。)因此,我们可以编写自己的单词边界测试。

在其他地方提出的解决方案,使用 regexp /(\W|^)(fancy namé|namé)(\W|$)/ig,在诸如“nameé”之类的文本的情况下会给出误报。

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;
  }
});
于 2010-07-19T10:25:27.183 回答
0

如果你想匹配“my_word”,你可以使用负向向后看?<!和负向向前看?!

这将检查单词前面没有非单词字符并且后面没有非单词字符 new RegExp(`(?<![A-Za-zÀ-ÖØ-öø-ÿ])my_word(?![A-Za-zÀ-ÖØ-öø-ÿ])`, "gi");

-是 ascii 表中的间隔。在这里检查它的 Ascii 表是你需要的 http://seamons.com/projects/js/ascii_table.html

于 2020-06-05T12:57:57.373 回答
0

正如其他回答者已经指出的那样,JS 正则表达式引擎不会将“é”视为单词字符。既然是这种情况,并且如果该字母后面跟着另一个非单词字符,您想要匹配,您可以在那里使用\B断言

> "Goal: Some Fancy Namé. Awesome.".replace(/\b(Fancy Namé|Namé)\B/i, '<a href="#">$1</a>');
'Goal: Some <a href="#">Fancy Namé</a>. Awesome.'

如果您希望其意图显而易见,可能不是最好的代码,但它在这种情况下有效,呵呵。

于 2021-02-12T19:38:21.780 回答
-1

也许在使用正则表达式时尝试使用\oor\x标志。

Javascript 正则表达式参考的结尾可能会对您有所帮助。

至于实际的八进制/十六进制值与什么é相关联,我不确定。

于 2010-03-15T19:30:37.723 回答