Мне нужно выделить ключевые слова в строке JavaScript без учета регистра.
Например:
highlight("foobar Foo bar FOO", "foo") должен вернуть "<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"Мне нужен код для работы с любым ключевым словом, и поэтому использование жестко запрограммированного регулярного выражения, такого как /foo/i, не является достаточным решением.
Как это сделать проще всего?
(Это пример более общей проблемы, подробно описанной в заголовке, но я считаю, что лучше всего рассмотреть ее на конкретном, полезном примере.)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы может используете регулярные выражения, если вы готовите строку поиска. Например, в PHP есть функция preg_quote, которая заменяет все символы регулярных выражений в строке их экранированными версиями.
Вот такая функция для javascript (источник):
function preg_quote (str, delimiter) {
// discuss at: https://locutus.io/php/preg_quote/
// original by: booeyOH
// improved by: Ates Goral (https://magnetiq.com)
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: Brett Zamir (https://brett-zamir.me)
// bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
// example 1: preg_quote("$40")
// returns 1: '\\$40'
// example 2: preg_quote("*RRRING* Hello?")
// returns 2: '\\*RRRING\\* Hello\\?'
// example 3: preg_quote("\\.+*?[^]$(){}=!<>|:")
// returns 3: '\\\\\\.\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:'
return (str + '')
.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&')
}
Итак, вы могли сделать следующее:
function highlight(str, search) {
return str.replace(new RegExp("(" + preg_quote(search) + ")", 'gi'), "<b>$1</b>");
}
В String.replace действительно есть метод «flags», но он нестандартный, а значит, ненадежный. Лучшим подходом было бы создание метода «полифил», который выбирает подходящий вариант.
@YellowAfterlife Здесь флаги передаются регулярному выражению, а не функции String.replace, так что все в порядке :)
Почему бы просто не создавать новое регулярное выражение при каждом вызове вашей функции? Вы можете использовать:
new Regex([pat], [flags])
где [pat] - строка для шаблона, а [flags] - флаги.
function highlightWords( line, word )
{
var regex = new RegExp( '(' + word + ')', 'gi' );
return line.replace( regex, "<b>$1</b>" );
}
Конечно, вам нужно быть осторожным с тем, что вы заменяете и что ищете в заметках @bobince. Вышеупомянутое будет хорошо работать для обычного текста и большинства поисков, если вы осторожно цитируете свои символы регулярного выражения ...
Это вызовет проблемы, если в заменяемом слове есть символы регулярного выражения. Решение @koman обходит это.
Это не работает, если работа является точкой или точкой, как заставить ее работать в случае точки или точки, или нескольких точек (например: "...")
@helpme - это специальные символы в регулярном выражении. Вам нужно сначала процитировать их, используя обратную косую черту. Обратите внимание, что обратная косая черта также является символом кавычек в строке, поэтому вам нужно использовать два в строке замены. word.replace(/\./g, '\\.')
Регулярные выражения хороши, если ключевые слова на самом деле являются словами, вы можете просто использовать конструктор RegExp вместо литерала, чтобы создать его из переменной:
var re= new RegExp('('+word+')', 'gi');
return s.replace(re, '<b>$1</b>');
Сложность возникает, если в "ключевых словах" могут быть знаки препинания, поскольку пунктуация имеет особое значение в регулярных выражениях. К сожалению, в отличие от большинства других языков / библиотек с поддержкой регулярных выражений, в JavaScript нет стандартной функции, позволяющей избежать знаков препинания для регулярных выражений.
И вы не можете быть полностью уверены, какие символы нужно экранировать, потому что не все реализации regexp в браузерах гарантированно будут одинаковыми. (В частности, новые браузеры могут добавлять новые функции.) И экранирующие обратную косую черту символы, которые не являются специальными, не гарантируют, что по-прежнему будут работать, хотя на практике это так.
Итак, лучшее, что вы можете сделать, это одно из:
Однако если вы используете это для выделения слов в HTML, в которых уже есть разметка, у вас возникнут проблемы. Ваше «слово» может появиться в имени элемента или значении атрибута, и в этом случае попытка обернуть его <b> приведет к поломке. В более сложных сценариях возможно даже HTML-инъекция в дыру в безопасности XSS. Если вам нужно справиться с разметкой, вам понадобится более сложный подход, разделив разметку «<...>», прежде чем пытаться обрабатывать каждый фрагмент текста отдельно.
Вы можете улучшить объект RegExp с помощью функции, которая выполняет экранирование специальных символов за вас:
RegExp.escape = function(str)
{
var specials = /[.*+?|()\[\]{}\\$^]/g; // .*+?|()[]{}\$^
return str.replace(specials, "\\$&");
}
Тогда вы сможете без проблем использовать то, что предложили другие:
function highlightWordsNoCase(line, word)
{
var regex = new RegExp("(" + RegExp.escape(word) + ")", "gi");
return line.replace(regex, "<b>$1</b>");
}
? в javascript RegExp необходимо экранировать двойной обратной косой чертой, например \\?
@Jerinaw Как вы думаете, что делает моя функция RegExp.escape?
@Jerinaw Фактически, вам нужно избегать вопросительного знака только один раз для регулярного выражения, поэтому вы получите \?, когда используете литерал регулярного выражения. Но вам нужно избегать обратной косой черты сам для строк JS, поэтому вы получите \\? при построении регулярного выражения из строки. И да, в классе символов единственный символ, который вы действительно избегаете с помощью должен, - это ].
пожалуйста, не поощряйте исправление обезьян в javascript
Как насчет чего-то вроде этого:
if (typeof String.prototype.highlight !== 'function') {
String.prototype.highlight = function(match, spanClass) {
var pattern = new RegExp( match, "gi" );
replacement = "<span class='" + spanClass + "'>$&</span>";
return this.replace(pattern, replacement);
}
}
Тогда это можно было бы назвать так:
var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight");
Для бедных с дисрегексией или регексофобией:
function replacei(str, sub, f){
let A = str.toLowerCase().split(sub.toLowerCase());
let B = [];
let x = 0;
for (let i = 0; i < A.length; i++) {
let n = A[i].length;
B.push(str.substr(x, n));
if (i < A.length-1)
B.push(f(str.substr(x + n, sub.length)));
x += n + sub.length;
}
return B.join('');
}
s = 'Foo and FOO (and foo) are all -- Foo.'
t = replacei(s, 'Foo', sub=>'<'+sub+'>')
console.info(t)Выход:
<Foo> and <FOO> (and <foo>) are all -- <Foo>.
Вам вообще не следует использовать для этого RegExp. Вы можете передать флаги
'gi'в качестве третьего аргументаreplace. Вам не нужно использоватьpreg_quoteили создаватьRegExpили что-то в этом роде.