Неверная замена строки в JavaScript?
Мне нужно выделить, нечувствительно к регистру, заданные ключевые слова в строке JavaScript.
Например:
-
highlight("foobar Foo bar FOO", "foo")
должен возвращать "<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"
Мне нужно, чтобы код работал для любого ключевого слова, и поэтому использование жестко закодированного регулярного выражения, такого как /foo/i
, не является достаточным решением.
Каков самый простой способ сделать это?
(Это пример более общей проблемы, описанной в названии, но я считаю, что лучше всего решать конкретный, полезный пример.)
Ответы
Ответ 1
При подготовке строки поиска вы можете использовать регулярные выражения. В PHP, например. существует функция preg_quote, которая заменяет все символы регулярных выражений в строке с их экранированными версиями.
Вот такая функция для javascript:
function preg_quote( str ) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// * 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(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
}
(взято из http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_preg_quote/)
Итак, вы можете сделать следующее:
function highlight( data, search )
{
return data.replace( new RegExp( "(" + preg_quote( search ) + ")" , 'gi' ), "<b>$1</b>" );
}
Ответ 2
function highlightWords( line, word )
{
var regex = new RegExp( '(' + word + ')', 'gi' );
return line.replace( regex, "<b>$1</b>" );
}
Ответ 3
Вы можете улучшить объект 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>");
}
Ответ 4
Регулярные выражения прекрасны, пока ключевые слова - это действительно слова, вы можете просто использовать конструктор RegExp вместо литерала для создания одного из переменной:
var re= new RegExp('('+word+')', 'gi');
return s.replace(re, '<b>$1</b>');
Трудность возникает, если "ключевые слова могут иметь пунктуацию, поскольку пунктуация имеет тенденцию иметь особое значение в регулярных выражениях. К сожалению, в отличие от большинства других языков/библиотек с поддержкой regexp, нет стандартной функции для удаления пунктуации для регулярных выражений в JavaScript.
И вы не можете быть полностью уверены, какие именно символы нуждаются в экранировании, потому что не каждая реализация regexp в браузере гарантирована точно такой же. (В частности, новые браузеры могут добавлять новые функциональные возможности.) И обратные слэш-символы, которые не являются особенными, не гарантируют работу, хотя на практике это происходит.
Итак, самое лучшее, что вы можете сделать, это одно из:
- попытка поймать каждый специальный символ в общем использовании браузера сегодня [добавить: см. рецепт Себастьяна]
- обратная косая черта - избегайте всех не-буквенных символов. care:\W также будет соответствовать символам Unicode, отличным от ASCII, чего вы действительно не хотите.
- просто убедитесь, что в ключевом слове нет не-буквенно-цифровых символов, прежде чем искать
Если вы используете это, чтобы выделить слова в HTML, которые уже имеют разметку, у вас проблемы. Ваше слово может отображаться в имени элемента или значении атрибута, и в этом случае попытка обернуть <b> вокруг него вызовет раскол. В более сложных сценариях возможно даже инъекция HTML в дыру безопасности XSS. Если вам нужно справиться с разметкой, вам понадобится более сложный подход, разделяющий '<... > , прежде чем пытаться обрабатывать каждый фрагмент текста самостоятельно.
Ответ 5
Что-то вроде этого:
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");
Ответ 6
Для бедняков с дисрексией или реджексофобией:
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.log(t)
Ответ 7
Почему бы просто не создать новое регулярное выражение для каждого вызова вашей функции? Вы можете использовать:
new Regex([pat], [flags])
где [pat] - строка для шаблона, а [flags] - флаги.