Замена строки без учета регистра в JavaScript?


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

например:

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

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

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

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

7   51   2008-11-11 15:49:01

7 ответов:

вы можете использовать регулярные выражения, если вы готовите строку поиска. В PHP, например, есть функция preg_quote, которая заменяет все regex-символы в строке с их экранированными версиями.

вот такая функция для 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("");
    // *     returns 1: '$40'
    // *     example 2: preg_quote("*RRRING* Hello?");
    // *     returns 2: '\*RRRING\* Hello\?'
    // *     example 3: preg_quote("\.+*?[^]$(){}=!<>|:");
    // *     returns 3: '\\.\+\*\?\[\^\]$\(\)\{\}\=\!\<\>\|\:'

    return (str+'').replace(/([\\.\+\*\?\[\^\]$\(\)\{\}\=\!\<\>\|\:])/g, "\");
}

(взято из 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></b>" );
}
function highlightWords( line, word )
{
     var regex = new RegExp( '(' + word + ')', 'gi' );
     return line.replace( regex, "<b></b>" );
}

Вы можете улучшить объект 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></b>");
}

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

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

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

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

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

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

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

Как насчет чего-то вроде этого:

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.log(t)

выход:

<Foo> and <FOO> (and <foo>) are all -- <Foo>.

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

new Regex([pat], [flags])

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