web-dev-qa-db-fra.com

Remplacement de chaîne insensible à la casse en JavaScript?

Je dois mettre en évidence, sans tenir compte de la casse, les mots clés donnés dans une chaîne JavaScript.

Par exemple:

  • highlight("foobar Foo bar FOO", "foo") devrait retourner "<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"

J'ai besoin du code pour travailler avec n'importe quel mot clé, et donc utiliser une expression régulière codée en dur comme /foo/i n'est pas une solution suffisante.

Quelle est la manière la plus simple de faire ça?

(Ceci est un exemple d'un problème plus général détaillé dans le titre, mais je pense qu'il est préférable de s'attaquer à un exemple concret et utile.)

51
Antti Syk&#228;ri

Vous pouvez utiliser des expressions régulières si vous préparez la chaîne de recherche. Dans PHP par exemple, il y a une fonction preg_quote, qui remplace tous les regex-chars dans une chaîne avec leurs versions échappées.

Voici une telle fonction pour 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");
}

(Tiré de http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_preg_quote/ )

Vous pouvez donc faire ce qui suit:

function highlight( data, search )
{
    return data.replace( new RegExp( "(" + preg_quote( search ) + ")" , 'gi' ), "<b>$1</b>" );
}
69
okoman
function highlightWords( line, Word )
{
     var regex = new RegExp( '(' + Word + ')', 'gi' );
     return line.replace( regex, "<b>$1</b>" );
}
64
tvanfosson

Vous pouvez améliorer l'objet RegExp avec une fonction qui effectue un échappement de caractère spécial pour vous:

RegExp.escape = function(str) 
{
  var specials = /[.*+?|()\[\]{}\\$^]/g; // .*+?|()[]{}\$^
  return str.replace(specials, "\\$&");
}

Ensuite, vous pourrez utiliser ce que les autres ont suggéré sans aucun souci:

function highlightWordsNoCase(line, Word)
{
  var regex = new RegExp("(" + RegExp.escape(Word) + ")", "gi");
  return line.replace(regex, "<b>$1</b>");
}
13
Tomalak

Les expressions régulières sont bien tant que les mots clés sont vraiment des mots, vous pouvez simplement utiliser un constructeur RegExp au lieu d'un littéral pour en créer un à partir d'une variable:

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

La difficulté survient si les "mots clés" peuvent contenir de la ponctuation, car la ponctuation a généralement une signification particulière dans les expressions régulières. Malheureusement, contrairement à la plupart des autres langues/bibliothèques avec prise en charge des expressions rationnelles, il n'y a pas de fonction standard pour échapper à la ponctuation des expressions rationnelles en JavaScript.

Et vous ne pouvez pas être totalement sûr de ce que les caractères doivent échapper, car toutes les implémentations de regexp de navigateur ne sont pas exactement identiques. (En particulier, les nouveaux navigateurs peuvent ajouter de nouvelles fonctionnalités.) Et les caractères d'échappement antislash qui ne sont pas spéciaux ne sont pas garantis pour continuer à fonctionner, bien que dans la pratique, c'est le cas.

Donc, le mieux que vous puissiez faire est l'un des suivants:

  • essayer d'attraper chaque caractère spécial dans une utilisation courante du navigateur aujourd'hui [ajouter: voir la recette de Sebastian]
  • barre oblique inverse échappe à tous les caractères non alphanumériques. care:\W correspondra également aux caractères Unicode non ASCII, ce que vous ne voulez pas vraiment.
  • assurez-vous simplement qu'il n'y a pas de caractères non alphanumériques dans le mot clé avant de rechercher

Si vous l'utilisez pour mettre en surbrillance des mots en HTML qui contiennent déjà du balisage, vous rencontrez des problèmes. Votre "mot" peut apparaître dans un nom d'élément ou une valeur d'attribut, auquel cas une tentative d'enrouler un <b> autour de lui entraînera une cassure. Dans des scénarios plus compliqués, peut-être même une injection HTML dans une faille de sécurité XSS. Si vous devez gérer le balisage, vous aurez besoin d'une approche plus compliquée, divisant le balisage "<...>" avant d'essayer de traiter chaque tronçon de texte seul.

5
bobince

Et quelque chose comme ça:

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);
  }
}

Cela pourrait alors être appelé ainsi:

var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight");
5
GitCarter

Pour les pauvres souffrant de disregexia ou regexophobia:

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)

Production:

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

Pourquoi ne pas simplement créer une nouvelle expression régulière à chaque appel à votre fonction? Vous pouvez utiliser:

new Regex([pat], [flags])

où [pat] est une chaîne pour le motif et [drapeaux] sont les drapeaux.

0
Erik Hesselink