Замена строки без учета регистра в JavaScript? - PullRequest
51 голосов
/ 11 ноября 2008

Мне нужно выделить без учета регистра заданные ключевые слова в строке JavaScript.

Например:

  • highlight("foobar Foo bar FOO", "foo") должен вернуться "<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"

Мне нужен код для работы с любым ключевым словом, и поэтому использование жестко закодированного регулярного выражения типа /foo/i не является достаточным решением.

Какой самый простой способ сделать это?

(Это пример более общей проблемы, подробно изложенной в заголовке, но я чувствую, что лучше заняться конкретным, полезным примером.)

Ответы [ 7 ]

69 голосов
/ 11 ноября 2008

Вы можете использовать регулярные выражения, если вы готовите строку поиска. Например, в 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>" );
}
64 голосов
/ 11 ноября 2008
function highlightWords( line, word )
{
     var regex = new RegExp( '(' + word + ')', 'gi' );
     return line.replace( regex, "<b>$1</b>" );
}
13 голосов
/ 11 ноября 2008

Вы можете улучшить объект 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>");
}
5 голосов
/ 15 сентября 2012

Что-то вроде этого:

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");
5 голосов
/ 11 ноября 2008

Регулярные выражения хороши, если ключевые слова действительно являются словами, вы можете просто использовать конструктор RegExp вместо литерала, чтобы создать его из переменной:

var re= new RegExp('('+word+')', 'gi');
return s.replace(re, '<b>$1</b>');

Трудность возникает, если в «ключевых словах» может быть пунктуация, поскольку в регулярных выражениях пунктуация имеет особое значение. К сожалению, в отличие от большинства других языков / библиотек с поддержкой регулярных выражений, в JavaScript нет стандартной функции, позволяющей избежать пунктуации для регулярных выражений.

И вы не можете быть полностью уверены, какие именно символы нужно экранировать, потому что не все браузерные реализации регулярных выражений гарантированно будут одинаковыми. (В частности, более новые браузеры могут добавлять новые функции.) И символы, не являющиеся специальными, без обратной косой черты не гарантируют, что все еще будут работать, хотя на практике это так.

Итак, самое лучшее, что вы можете сделать, это одно из:

  • пытается поймать каждый специальный символ в обычном браузерном использовании сегодня [добавить: см рецепт Себастьяна]
  • обратный слеш - экранирование всех не алфавитно-цифровых символов. care: \ W также будет соответствовать не-ASCII символам Юникода, которые вам не нужны.
  • просто убедитесь, что в ключевом слове нет не алфавитно-цифровых символов перед поиском

Если вы используете это для выделения слов в HTML, которые уже содержат разметку, у вас возникнут проблемы. Ваше «слово» может появиться в имени элемента или значении атрибута, и в этом случае попытка обернуть вокруг него приведет к поломке. В более сложных сценариях возможно даже HTML-инъекция в дыру в безопасности XSS. Если вам нужно справиться с разметкой, вам понадобится более сложный подход, разделив разметку «<...>», прежде чем пытаться обрабатывать каждый фрагмент текста самостоятельно.

1 голос
/ 14 февраля 2017

Для бедных с дисгексией или регексофобией:

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)

Выход:

<Foo> and <FOO> (and <foo>) are all -- <Foo>.
0 голосов
/ 11 ноября 2008

Почему бы просто не создавать новое регулярное выражение при каждом вызове вашей функции? Вы можете использовать:

new Regex([pat], [flags])

где [pat] - строка для шаблона, а [flags] - флаги.

...