Javascript RegExp + Word边界+ unicode字符

时间:2012-05-14 19:50:35

标签: javascript regex unicode

我正在构建搜索,我将使用javascript自动完成功能。我来自芬兰(芬兰语)所以我必须处理一些特殊的角色,如ä,ö和å

当用户在搜索输入字段中键入文本时,我会尝试将文本与数据匹配。

如果用户输入例如“ää”,这是一个不能正常工作的简单示例。 “äl”

也是如此
var title = "this is simple string with finnish word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";

// does not work
//var searchterm = "ää";

// Works
//var searchterm = "wi";

if ( new RegExp("\\b"+searchterm, "gi").test(title) ) {
    $("#result").html("Match: ("+searchterm+"): "+title);
} else {
    $("#result").html("nothing found with term: "+searchterm);   
}

http://jsfiddle.net/7TsxB/

那么如何让这些ä,ö和å字符与javascript正则表达式一起使用?

我想我应该使用unicode代码但是我应该怎么做?这些字符的代码是: [\ u00C4,\ u00E4,\ u00C5,\ u00E5,\ u00D6,\ u00F6]

=> äÄåÅöÖ

11 个答案:

答案 0 :(得分:38)

Regex似乎存在问题,并且字符\b与字符串的开头匹配,其起始字符超出正常的256字节范围。

请尝试使用\b

,而不是使用(?:^|\\s)
var title = "this is simple string with finnish word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";

// does not work
//var searchterm = "ää";

// Works
//var searchterm = "wi";

if ( new RegExp("(?:^|\\s)"+searchterm, "gi").test(title) ) {
    $("#result").html("Match: ("+searchterm+"): "+title);
} else {
    $("#result").html("nothing found with term: "+searchterm);   
}

故障:

(?:括号()在Regex中形成一个捕获组。括号以问号开头,冒号?:构成非捕获组。他们只是将这些术语组合在一起

^插入符号匹配字符串的开头

|栏是“或”运算符。

\s匹配空格(在字符串中显示为\\s,因为我们必须转义反斜杠)

)关闭群组

因此,我们不使用匹配单词边界且不适用于unicode字符的\b,而是使用与字符串OR空格的开头匹配的非捕获组。

答案 1 :(得分:16)

JavaScript RegEx中的\b字符类实际上只对简单的ASCII编码有用。 \b\w\W集或\w之间的边界以及字符串的开头或结尾的快捷代码。这些字符集仅考虑ASCII“单词”字符,其中\w等于[a-zA-Z0-9_]\W是该类的否定。

这使得RegEx角色类在处理任何真实语言方面基本无用。

\s应该适用于您想要做的事情,前提是搜索字词仅由空格分隔。

答案 2 :(得分:10)

这个问题很老,但我认为我找到了一个更好的解决方案,用于使用unicode字母的正则表达式中的边界。 使用XRegExp,您可以实现扩展此

的有效\ b边界
XRegExp('(?=^|$|[^\\p{L}])')

结果是长达4000多个字符,但它看起来效果很好。

一些解释:(?=)是一个零长度前瞻,它查找开始或结束边界或非字母unicode字符。最重要的想法是前瞻,因为\ b不能捕获任何东西:它只是真或假。

答案 3 :(得分:6)

当你必须使用Unicode中的一组特定字符时,我建议你使用XRegExp,这个库的作者映射了所有类型的区域字符集,使得使用不同语言的工作更容易。< / p>

答案 4 :(得分:2)

使用Unicode时,我发现\b有些奇怪:

/\bo/.test("pop"); // false (obviously)
/\bä/.test("päp"); // true (what..?)

/\Bo/.test("pop"); // true
/\Bä/.test("päp"); // false (what..?)

似乎\b\B的含义相反,但仅在与非ASCII Unicode一起使用时才有效?可能会有更深层次的事情发生,但我不确定它是什么。

在任何情况下,似乎单词边界都是问题,而不是Unicode字符本身。也许您应该将\b替换为(^|[\s\\/-_&]),因为这似乎可以正常工作。 (但是,使你的符号列表比我的更全面。)

答案 5 :(得分:1)

\b是字母和非字母字符之间转换的快捷方式,反之亦然。

更新和完善max_masseti的答案:

通过在ES2018中为RegExs引入/u修饰符,您现在可以使用\p{L}表示任何unicode字母和\P{L}(注意大写的P)代表任何东西。

EDIT :先前版本不完整。

因此:

const text = 'A Fé, o Império, e as terras viciosas';

text.split(/(?<=\p{L})(?=\P{L})|(?<=\P{L})(?=\p{L})/);

// ['A', ' Fé', ',', ' o', ' Império', ',', ' e', ' as', ' terras', ' viciosas']

我们使用后退(?<=...)查找字母,并使用前瞻(?=...)查找非字母,反之亦然。

答案 6 :(得分:1)

尽管该问题似乎已经存在了8年,但不久前我遇到了一个类似的问题(我必须匹配西里尔字母)。我花了一整天的时间,在StackOverflow上找不到任何合适的答案。因此,为了避免其他人付出很大的努力,我想分享一下我的解决方案。

是的,\b单词边界仅适用于拉丁字母(Word boundary: \b):

单词边界\ b不适用于非拉丁字母 单词边界测试\ b检查位置的一侧应有\ w,另一侧应是“ not \ w”。 但是\ w表示拉丁字母a-z(或数字或下划线),因此该测试不适用于其他字符,例如西里尔字母或象形文字。

是的,JavaScript RegExp实现几乎不支持UTF-8编码。

因此,我尝试在非拉丁字符的支持下实现自己的单词边界功能。为了使单词边界仅适用于西里尔字母,我创建了以下正则表达式:

new RegExp(`(?<![\u0400-\u04ff])${cyrillicSearchValue}(?![\u0400-\u04ff])`,'gi')

\u0400-\u04fftable of codes中提供的一系列西里尔字符。这不是理想的解决方案,但是,在大多数情况下都可以正常工作。

要使其适合您的情况,您只需要从list of Unicode characters中选取适当范围的代码即可。

要试用我的示例,请运行以下代码段。

function getMatchExpression(cyrillicSearchValue) {
  return new RegExp(
    `(?<![\u0400-\u04ff])${cyrillicSearchValue}(?![\u0400-\u04ff])`,
    'gi',
  );
}

const sentence = 'Будь-який текст кирилицею, де необхідно знайти слово з контексту';

console.log(sentence.match(getMatchExpression('текст')));
// expected output: ["текст"]


console.log(sentence.match(getMatchExpression('но')));
// expected output: null

答案 7 :(得分:0)

我的想法是使用代表芬兰字母的代码进行搜索

new RegExp("\\b"+asciiOnly(searchterm), "gi").test(asciiOnly(title))

我最初的想法是使用普通encodeURI,但%符号似乎会干扰正则表达式。

http://jsfiddle.net/7TsxB/5/

我使用encodeURI编写了一个粗略的函数来编码代码超过128的每个字符,但删除了它的%并添加了QQ&#39;在一开始的时候。它不是最好的标记,但我无法使用非字母数字。

答案 8 :(得分:0)

我遇到了类似的问题,但我不得不更换一系列术语。如果两个术语在文本中彼此相邻(因为它们的边界重叠),我发现的所有解决方案都没有用。所以我不得不使用一些改进的方法:

var text = "Ještě. že; \"už\" à. Fürs, 'anlässlich' že že že.";
var terms = ["à","anlässlich","Fürs","už","Ještě", "že"];
var replaced = [];
var order = 0;
for (i = 0; i < terms.length; i++) {
    terms[i] = "(^\|[ \n\r\t.,;'\"\+!?-])(" + terms[i] + ")([ \n\r\t.,;'\"\+!?-]+\|$)";
}
var re = new RegExp(terms.join("|"), "");
while (true) {
    var replacedString = "";
    text = text.replace(re, function replacer(match){
        var beginning = match.match("^[ \n\r\t.,;'\"\+!?-]+");
        if (beginning == null) beginning = "";
        var ending = match.match("[ \n\r\t.,;'\"\+!?-]+$");
        if (ending == null) ending = "";
        replacedString = match.replace(beginning,"");
        replacedString = replacedString.replace(ending,"");
        replaced.push(replacedString);
        return beginning+"{{"+order+"}}"+ending;
    });
if (replacedString == "") break;
order += 1;
}

查看小提琴中的代码:http://jsfiddle.net/antoninslejska/bvbLpdos/1/

正则表达式的灵感来自:http://breakthebit.org/post/3446894238/word-boundaries-in-javascripts-regular

我无法说,我发现解决方案很优雅......

答案 9 :(得分:0)

您正在寻找的是Unicode字边界标准:

http://unicode.org/reports/tr29/tr29-9.html#Word_Boundaries

这里有一个JavaScript实现(unciodejs.wordbreak.js)

https://github.com/wikimedia/unicodejs

答案 10 :(得分:0)

问题的正确答案由 andrefs 给出。 将所有必需的内容放在一起后,我只会更清楚地重写它。

对于ASCII文本,可以使用\b在模式的开头和结尾都匹配单词边界。使用Unicode文本时,您需要使用2种不同的模式来进行相同的操作:

  • 使用(?<=^|\P{L})来匹配主要模式之前的开头或单词边界。
  • 使用(?=\P{L}|$)匹配主模式的结尾或单词边界。
  • 此外,在所有内容的开头使用(?i),以使所有匹配项均不区分大小写。

因此,得出的答案是:(?i)(?<=^|\P{L})xxx(?=\P{L}|$),其中xxx是您的主要模式。这相当于ASCII文本的(?i)\bxxx\b

为使代码正常工作,您现在需要执行以下操作:

  • 将变量“ searchterm”,您要查找的模式或单词分配给您。
  • 转义变量的内容。例如,将'\'替换为'\\',并对正则表达式的任何保留特殊字符(例如'\^', '\$', '\/'等)执行相同的操作。检查here的相关问题这个。
  • 只需使用string.replace()方法,即可将变量的内容插入上面的模式中的“ xxx”位置。