[英]Javascript RegExp + Word boundaries + unicode characters
我正在构建搜索,我将使用 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);
}
那么如何让这些 ä、ö 和 å 字符与 javascript 正则表达式一起使用呢?
我想我应该使用 unicode 代码,但我该怎么做呢? 这些字符的代码是:[Ä,ä,Å,å,Ö,ö]
=> äÄåÅöÖ
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);
}
分解:
(?:
括号()
在正则表达式中形成一个捕获组。括号以问号和冒号开头?:
形成一个非捕获组。它们只是将术语组合在一起
^
插入符号匹配字符串的开头
|
条是“或”运算符。
\s
匹配空格(在字符串中显示为\\s
,因为我们必须转义反斜杠)
)
关闭组
因此,我们不使用匹配单词边界且不适用于 unicode 字符的\b
,而是使用匹配字符串开头或空格的非捕获组。
JavaScript RegEx 中的\b
字符类实际上只对简单的 ASCII 编码有用。 \b
是\w
和\W
集合或\w
和字符串的开头或结尾之间的边界的快捷代码。 这些字符集只考虑 ASCII “单词”字符,其中\w
等于[a-zA-Z0-9_]
并且\W
是该类的否定。
这使得 RegEx 字符类在处理任何真实语言时基本上没有用处。
\s
应该适用于您想要做的事情,前提是搜索词仅由空格分隔。
这个问题很老,但我想我找到了一个更好的解决方案,用于使用 unicode 字母的正则表达式中的边界。 使用 XRegExp 库,您可以实现一个有效的 \b 边界扩展它
XRegExp('(?=^|$|[^\\p{L}])')
结果是 4000+ 字符长,但它似乎工作得很好。
一些解释: (?= ) 是一个零长度的前瞻,它查找开始或结束边界或非字母 unicode 字符。 最重要的想法是前瞻,因为 \b 不捕获任何东西:它只是对或错。
\b
是字母和非字母字符之间转换的快捷方式,反之亦然。
更新和改进max_massti的答案:
随着 ES2018 中 RegEx 的/u
修饰符的引入,您现在可以使用\p{L}
来表示任何 unicode 字母,而\P{L}
(注意大写的P
)来表示除此之外的任何内容。
编辑:以前的版本不完整。
像这样:
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']
我们使用lookbehind (?<=...)
来查找字母和lookahead (?=...)
来查找非字母,反之亦然。
当您必须使用 Unicode 中的特定字符集时,我建议您使用XRegExp ,该库的作者映射了所有类型的区域字符集,从而使使用不同语言的工作更容易。
在使用 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\\/-_&])
,因为这似乎可以正常工作。 (不过,让你的符号列表比我的更全面。)
我的想法是使用代表芬兰字母的代码进行搜索
new RegExp("\\b"+asciiOnly(searchterm), "gi").test(asciiOnly(title))
我最初的想法是使用普通的encodeURI
但 % 符号似乎会干扰正则表达式。
我使用 encodeURI 编写了一个粗略的函数来编码超过 128 的代码的每个字符,但删除它的 % 并在开头添加“QQ”。 它不是最好的标记,但我无法让非字母数字工作。
您正在寻找的是 Unicode 字边界标准:
http://unicode.org/reports/tr29/tr29-9.html#Word_Boundaries
这里有一个 JavaScript 实现(unciodejs.wordbreak.js)
试图找到文本“myTest”:
/(?<?[\p{L}\p{N}_])myTest(?![\p{L}\p{N}_])/gu
类似NetBeans或Notepad++形式。 试图在表达式前后的任何 unicode 个字母和数字字符中找到没有任何字母或数字或下划线(如单词边界的\w
字符\b
)的表达式。
我遇到了类似的问题,但我不得不替换一系列术语。 如果两个术语在文本中彼此相邻(因为它们的边界重叠),我发现的所有解决方案都不起作用。 所以我不得不使用一些修改过的方法:
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
我不能说,我觉得解决方案很优雅......
该问题的正确答案由andrefs给出。 在将所有必需的东西放在一起之后,我只会更清楚地重写它。
对于 ASCII 文本,您可以使用\b
来匹配模式开头和结尾的单词边界。 使用 Unicode 文本时,您需要使用 2 种不同的模式来执行相同的操作:
(?<=^|\P{L})
匹配主要模式之前的开始或单词边界。(?=\P{L}|$)
匹配主模式之后的结尾或单词边界。(?i)
以使所有这些匹配不区分大小写。 所以得到的答案是: (?i)(?<=^|\P{L})xxx(?=\P{L}|$)
,其中 xxx 是您的主要模式。 这相当于 ASCII 文本的(?i)\bxxx\b
。
为了使您的代码正常工作,您现在需要执行以下操作:
'\'
替换为'\\'
并对正则表达式的任何保留特殊字符执行相同操作,例如'\^', '\$', '\/'
等。查看此处了解有关如何操作的问题去做这个。string.replace()
方法,将变量的内容插入到上面的模式中,代替“xxx”。我有一个类似的问题,我试图用不同的 unicode 词替换所有特定的 unicode 词,但我不能使用lookbehind,因为 JS 引擎不支持此代码。我最终像这样解决了它:
const needle = "КАРТОПЛЯ";
const replace = "БАРАБОЛЯ";
const regex = new RegExp(
String.raw`(^|[^\n\p{L}])`
+ needle
+ String.raw`(?=$|\P{L})`,
"gimu",
);
const result = (
'КАРТОПЛЯ сдффКАРТОПЛЯдадф КАРТОПЛЯ КАРТОПЛЯ КАРТОПЛЯ??? !!!КАРТОПЛЯ ;!;!КАРТОПЛЯ/#?#?'
+ '\n\nКАРТОПЛЯ КАРТОПЛЯ - - -КАРТОПЛЯ--'
)
.replace(regex, function (match, ...args) {
return args[0] + replace;
});
console.log(result)
输出:
БАРАБОЛЯ сдффКАРТОПЛЯдадф БАРАБОЛЯ БАРАБОЛЯ БАРАБОЛЯ??? !!!БАРАБОЛЯ ;!;!БАРАБОЛЯ/#?#?
БАРАБОЛЯ БАРАБОЛЯ - - -БАРАБОЛЯ--
第一个正则表达式: (^|[^\n\p{L}])
^|
= 行首或[^\n\p{L}]
= 任何不是字母或换行符的字符第二个正则表达式: (?=$|\P{L})
?=
= 前瞻$|
= 行尾或\P{L}
= 任何不是字母的字符第一个正则表达式捕获该组,然后通过args[0]
在替换期间将其放回字符串中,从而避免向后查找。 第二个正则表达式使用了前瞻。
请注意,第二个必须是前瞻,因为如果我们捕获它,则不会触发重叠的正则表达式匹配(例如КАРТОПЛЯ КАРТОПЛЯ КАРТОПЛЯ
只会匹配第一个和第三个)。
不好但有效:
var text = " аб аб АБ абвг ";
var ttt = "(аб)"
var p = "(^|$|[^A-Za-zА-Я-а-я0-9()])"; // add other word boundary symbols here
var exp = new RegExp(p+ttt+p,"gi");
text = text.replace(exp, "$1($2)$3").replace(exp, "$1($2)$3");
const t1 = performance.now();
console.log(text);
结果(没有引号):
" (аб) (аб) (АБ) абвг "
我为此苦苦挣扎。 使用法语重音字符,我设法找到了这个解决方案:
const myString = "MyString";
const regex = new RegExp(
"(?:[^À-ú]|^)\\b(" + myString + ")\\b(?:[^À-ú]|$)",
"ig"
);
id 的作用:它在“MyString”前后用\b
不断检查单词边界。 除此之外, (?:[^À-ú]|^)
和(?:[^À-ú]|$)
将检查 MyString 是否未被任何重音字符包围
它不适用于西里尔字母,但可能会找到西里尔字符的范围并因此编辑[^À-ú]
。
警告,它只捕获组(MyString)
,但总匹配包含前一个和下一个字符
参见示例: https://regex101.com/r/5P0ZIe/1
匹配示例:
MyString
Lorem ipsum. MyString dolor sit amet
(MyString)
BetweenCharactersMyStringIsNotFound
éMyStringé
ùMyString
MyStringÖ
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.