Вопрос по javascript, regex, unicode – Javascript RegExp + границы слов + символы Юникода

36

Я строю поиск, и я собираюсь использовать автозаполнение JavaScript с ним. Я из Финляндии (финский язык), поэтому мне приходится иметь дело с некоторыми специальными символами, такими как & # xE4 ;, & # xF6; и & # xE5;

Когда пользователь вводит текст в поле ввода поиска, я пытаюсь сопоставить текст с данными.

Вот простой пример, который работает неправильно, если пользователь вводит, например, & quot; & # xE4; & # xE4; & quot ;. То же самое с & quot; & # xE4; l & quot;

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/

Так как я могу получить эти & # xE4;, & # xF6; и & # xE5; символы для работы с регулярным выражением JavaScript?

Я думаю, что я должен использовать коды Unicode, но как мне это сделать? Коды для этих символов: [\ U00C4, \ u00E4, \ u00C5, \ u00E5, \ u00D6, \ u00F6]

= & GT; & # Xe4; & # XC4; & # xe5; & # xC5; & # XF6; & # xD6;

Я использую \ b, потому что хочу сопоставлять в начале каждого слова. user1394520
@Walkerneo:\b означает «граница слова» в регулярном выражении; косая черта здесь экранирована, потому что она находится в строке. apsillers
@apsillers, спасибо, странно, что я этого раньше не видел: / mowwwalker
Что с "\\ b" ? mowwwalker
Как вы видите, Javascript застрял в идиотском менталитете только в стиле ASCII 1960-х годов. Он не соответствует даже самым основным требованиям соответствия, необходимым для Уровня 1 & # x2019; s & # x201C; Базовая поддержка Юникода & # x201D; вUTS#18 on Unicode Regular Expressions, Попытка сделать настоящую работу по обработке текста Unicode в Javascript - ужасная и жестокая шутка: это невозможно. Упомянутый ниже плагин XRegexp необходим, но не достаточен для этих целей. tchrist

Ваш Ответ

8   ответов
2

\b при использовании Unicode:

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

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

Похоже, что значение\b а также\B инвертированы, но только при использовании с Unicode не ASCII? Здесь может быть что-то более глубокое, но я не уверен, что это такое.

В любом случае кажется, что проблема заключается в границе слова, а не в самих символах Юникода. Возможно, вам следует просто заменить\b с(^|[\s\\/-_&])кажется, что это работает правильно. (Сделайте ваш список символов более полным, чем мой.)

\b а также\B не поддерживают Unicode в JavaScript, поэтому они считаютä не буквенно-цифровой символ и, следовательно, увидеть границу слова междуp а такжеä.
38

\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 в строке, потому что мы должны избежать обратной косой черты)

) закрывает группу

Поэтому вместо использования\b, который соответствует границам слов и не работает для символов Юникода, мы используем группу без захвата, которая соответствует началу строки ИЛИ пробела.

Это НЕ правильное решение.(?:^|\\s) не утверждение нулевой ширины, как\b есть и будет потреблять символы из матча. Позитивный взгляд был бы лучшей идеей ((?=^|\\s)), но будет работать только после матча, так как lookbehind по-прежнему не поддерживается. Кроме того, границы слов - это не просто пробелы и строки, но и множество других символов.
Это похоже на работу! user1394520
@HDog хороший момент.
Есть ли причина не включать $ (конец строки) в регулярное выражение? То есть (?: ^ | \ S | $)
& quot; попробуйте это & quot; не является решением. Дайте некоторую информацию оwhy предложенное регулярное выражение работает. Что значит(?:^|\\s) действительно? Вы вообще не объясняете это решение.
6

XRegExp когда вам нужно работать с определенным набором символов из Unicode, автор этой библиотеки отобразил все виды региональных наборов символов, упрощая работу с различными языками.

это не очень хорошее решение для тех, кто хочет что-то сделать с подобранной подстрокой
Предполагает ли это, что любой не-ASCII символ является символом слова? Например, & quot; & xE4; l & quot; не будет восприниматься как начало слова в "& # xE4; lk & # xE4; & # xE4;", хотя так и должно быть.
Это отличная идея, и единственное, что сработало для меня. ВместоQQ Вы можете использовать управляющую строку___ что немного безопаснее и все же ascii, а вместоencodeURI Вы можете использовать родной JavaScriptescape/unescape методы, но в остальном это делает работу.
\b а также\B не поддерживают Unicode в JavaScript, поэтому они считаютä не буквенно-цифровой символ и, следовательно, увидеть границу слова междуp а такжеä.
9

но я думаю, что нашел лучшее решение для границы в регулярных выражениях с буквами юникода. Используя XRegExp, вы можете реализовать действительную границу \ b, расширяя это

XRegExp('(?=^|$|[^\\p{L}])')

В результате получается символ длиной более 4000 символов, но он работает довольно эффективно.

Некоторое объяснение: (? =) - это средство просмотра нулевой длины, которое ищет начальную или конечную границу или не буквенный символ Юникода. Самая важная мысль - это взгляд в будущее, потому что \ b ничего не захватывает: это просто истина или ложь.

0

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

Здесь есть реализация JavaScript (unciodejs.wordbreak.js)

https://github.com/wikimedia/unicodejs

17

\b класс символов в JavaScript RegEx действительно полезен только с простой кодировкой ASCII.\b это сокращенный код для границы между\w а также\W устанавливает или\w и начало или конец строки. Эти наборы символов учитывают только ASCII «слово» персонажи, где\w равно[a-zA-Z0-9_] а также\W это отрицание этого класса.

Это делает классы символов RegEx в значительной степени бесполезными для работы с любым реальным языком.

\s должен работать для того, что вы хотите сделать, при условии, что условия поиска ограничены только пробелами.

+1, но\b это не сокращение класса символов, как\w а также\sэто утверждение нулевой ширины, подобное\A, $и взгляды.
Это может помочь!github.com/joelarson4/CharFunk
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

Я не могу сказать, что нахожу решение элегантным ...

0

чтобы искать с кодами, представляющими финские буквы

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

Моя оригинальная идея заключалась в использовании простойencodeURI но знак%, казалось, мешал регулярному выражению.

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

Я написал грубую функцию, используя encodeURI для кодирования каждого символа с кодом более 128, но удалив его% и добавив 'QQ' в начале. Это не лучший маркер, но я не смог заставить работать не буквенно-цифровую форму.

Предполагает ли это, что любой не-ASCII символ является символом слова? Например, & quot; & xE4; l & quot; не будет восприниматься как начало слова в "& # xE4; lk & # xE4; & # xE4;", хотя так и должно быть.
это не очень хорошее решение для тех, кто хочет что-то сделать с подобранной подстрокой
Это отличная идея, и единственное, что сработало для меня. ВместоQQ Вы можете использовать управляющую строку___ что немного безопаснее и все же ascii, а вместоencodeURI Вы можете использовать родной JavaScriptescape/unescape методы, но в остальном это делает работу.

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