284 votes

Existe-t-il une version de String.indexOf() de JavaScript qui permet d'utiliser des expressions régulières ?

En javascript, existe-t-il un équivalent de String.indexOf() qui prend une expression régulière au lieu d'une chaîne pour le premier paramètre tout en autorisant un second paramètre ?

J'ai besoin de faire quelque chose comme

str.indexOf(/[abc]/ , i);

et

str.lastIndexOf(/[abc]/ , i);

Alors que String.search() prend un regexp comme paramètre, il ne me permet pas de spécifier un second argument !

Editar:
Cela s'est avéré plus difficile que je ne le pensais au départ, j'ai donc écrit une petite fonction de test pour tester toutes les solutions proposées... elle suppose que regexIndexOf et regexLastIndexOf ont été ajoutés à l'objet String.

function test (str) {
    var i = str.length +2;
    while (i--) {
        if (str.indexOf('a',i) != str.regexIndexOf(/a/,i)) 
            alert (['failed regexIndexOf ' , str,i , str.indexOf('a',i) , str.regexIndexOf(/a/,i)]) ;
        if (str.lastIndexOf('a',i) != str.regexLastIndexOf(/a/,i) ) 
            alert (['failed regexLastIndexOf ' , str,i,str.lastIndexOf('a',i) , str.regexLastIndexOf(/a/,i)]) ;
    }
}

et je teste comme suit pour m'assurer qu'au moins pour une regexp d'un caractère, le résultat est le même que si nous utilisions indexOf

//Chercher le a parmi les xes
test('xxx') ;
test('axx') ;
test('xax') ;
test('xxa') ;
test('axa') ;
test('xaa') ;
test('aax') ;
test('aaa') ;

0 votes

| à l'intérieur de [ ] correspond au caractère littéral | . Vous vouliez probablement dire [abc] .

0 votes

Oui merci vous avez raison, je vais le corriger mais la regexp elle-même n'est pas pertinente....

0 votes

J'ai mis à jour ma réponse Pat, merci pour tout commentaire.

242voto

Glenn Points 3787

Instances de la String ont un .search() méthode qui accepte un RegExp et renvoie l'index de la première correspondance.

Pour lancer la recherche à partir d'une position particulière (en simulant le deuxième paramètre de l'option .indexOf() ) vous pouvez slice du premier i des personnages :

str.slice(i).search(/re/)

Mais cela donnera l'index de la chaîne la plus courte (après que la première partie ait été coupée). Vous devrez donc ajouter la longueur de la partie coupée ( i ) à l'index retourné s'il ne l'était pas. -1 . Vous obtiendrez ainsi l'indice dans la chaîne de caractères d'origine :

function regexIndexOf(text, re, i) {
    var indexInSuffix = text.slice(i).search(re);
    return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i;
}

2 votes

De la question : Alors que String.search() prend un regexp comme paramètre, il ne me permet pas de spécifier un second argument !

15 votes

Str.substr(i).search(/re/)

7 votes

Excellente solution, mais le résultat est un peu différent. indexOf renvoie un nombre depuis le début (sans tenir compte du décalage), alors que ceci renvoie la position depuis le décalage. Donc, pour la parité, vous voudrez quelque chose comme ceci : function regexIndexOf(text, offset) { var initial = text.substr(offset).search(/re/); if(initial >= 0) { initial += offset; } return initial; }

159voto

Jason Bunting Points 27534

En combinant quelques-unes des approches déjà mentionnées (l'indexOf est évidemment assez simple), je pense que ce sont les fonctions qui feront l'affaire :

function regexIndexOf(string, regex, startpos) {
    var indexOf = string.substring(startpos || 0).search(regex);
    return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf;
}

function regexLastIndexOf(string, regex, startpos) {
    regex = (regex.global) ? regex : new RegExp(regex.source, "g" + (regex.ignoreCase ? "i" : "") + (regex.multiLine ? "m" : ""));
    if(typeof (startpos) == "undefined") {
        startpos = string.length;
    } else if(startpos < 0) {
        startpos = 0;
    }
    var stringToWorkWith = string.substring(0, startpos + 1);
    var lastIndexOf = -1;
    var nextStop = 0;
    while((result = regex.exec(stringToWorkWith)) != null) {
        lastIndexOf = result.index;
        regex.lastIndex = ++nextStop;
    }
    return lastIndexOf;
}

UPDATE : Edited regexLastIndexOf() de sorte qu'il semble imiter lastIndexOf() maintenant. Veuillez me faire savoir si cela échoue encore et dans quelles circonstances.


UPDATE : Passe tous les tests trouvés dans les commentaires de cette page, et les miens. Bien sûr, cela ne veut pas dire qu'il est à l'épreuve des balles. Tout commentaire est apprécié.

0 votes

Votre regexLastIndexOf ne retournera que l'index du dernier sans chevauchement match.

0 votes

Désolé, je ne suis pas un grand spécialiste des regex. Pouvez-vous me donner un exemple qui ferait échouer le mien ? J'apprécie de pouvoir en apprendre davantage, mais votre réponse n'aide pas quelqu'un d'aussi ignorant que moi. :)

0 votes

Jason, je viens d'ajouter une fonction à tester dans la question. Elle échoue (parmi d'autres tests) à la condition suivante 'axx'.lastIndexOf('a',2) != 'axx'.regexLastIndexOf(/a/,2)

57voto

pmrotule Points 144

J'ai une version courte pour vous. Elle fonctionne bien pour moi !

var match      = str.match(/[abc]/gi);
var firstIndex = str.indexOf(match[0]);
var lastIndex  = str.lastIndexOf(match[match.length-1]);

Et si vous voulez une version prototype :

String.prototype.indexOfRegex = function(regex){
  var match = this.match(regex);
  return match ? this.indexOf(match[0]) : -1;
}

String.prototype.lastIndexOfRegex = function(regex){
  var match = this.match(regex);
  return match ? this.lastIndexOf(match[match.length-1]) : -1;
}

EDITAR : si vous voulez ajouter le support pour fromIndex

String.prototype.indexOfRegex = function(regex, fromIndex){
  var str = fromIndex ? this.substring(fromIndex) : this;
  var match = str.match(regex);
  return match ? str.indexOf(match[0]) + fromIndex : -1;
}

String.prototype.lastIndexOfRegex = function(regex, fromIndex){
  var str = fromIndex ? this.substring(0, fromIndex) : this;
  var match = str.match(regex);
  return match ? str.lastIndexOf(match[match.length-1]) : -1;
}

Pour l'utiliser, c'est aussi simple que cela :

var firstIndex = str.indexOfRegex(/[abc]/gi);
var lastIndex  = str.lastIndexOfRegex(/[abc]/gi);

0 votes

C'est en fait un bon truc. Ce serait bien si vous l'élargissiez pour prendre aussi le startIndex paramètre comme d'habitude indeoxOf y lastIndexOf faire.

0 votes

@RobertKoritnik - j'ai édité ma réponse pour soutenir startIndex (ou fromIndex ). J'espère que cela vous aidera !

0 votes

lastIndexOfRegex doit également rajouter la valeur de fromIndex au résultat.

7voto

Markus Jarderot Points 33893

D'après la réponse de BaileyP. La principale différence est que ces méthodes renvoient -1 si le motif ne peut pas être trouvé.

Editar: Grâce à la réponse de Jason Bunting, j'ai eu une idée. Pourquoi ne pas modifier le .lastIndex de la regex ? Bien que cela ne fonctionne que pour les modèles avec le drapeau global ( /g ).

Editar: Mis à jour pour passer les test-cases.

String.prototype.regexIndexOf = function(re, startPos) {
    startPos = startPos || 0;

    if (!re.global) {
        var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":"");
        re = new RegExp(re.source, flags);
    }

    re.lastIndex = startPos;
    var match = re.exec(this);

    if (match) return match.index;
    else return -1;
}

String.prototype.regexLastIndexOf = function(re, startPos) {
    startPos = startPos === undefined ? this.length : startPos;

    if (!re.global) {
        var flags = "g" + (re.multiline?"m":"") + (re.ignoreCase?"i":"");
        re = new RegExp(re.source, flags);
    }

    var lastSuccess = -1;
    for (var pos = 0; pos <= startPos; pos++) {
        re.lastIndex = pos;

        var match = re.exec(this);
        if (!match) break;

        pos = match.index;
        if (pos <= startPos) lastSuccess = pos;
    }

    return lastSuccess;
}

0 votes

Cela semble le plus prometteur jusqu'à présent (après quelques corrections de la sytaxe) :-) Seuls quelques tests échouent sur les conditions de bord. Des choses comme 'axx'.lastIndexOf('a',0) != 'axx'.regexLastIndexOf(/a/,0) ... Je regarde pour voir si je peux corriger ces cas.

4voto

Peter Bailey Points 62125

Ce n'est pas le cas à l'origine, mais vous pouvez certainement ajouter cette fonctionnalité.

<script type="text/javascript">

String.prototype.regexIndexOf = function( pattern, startIndex )
{
    startIndex = startIndex || 0;
    var searchResult = this.substr( startIndex ).search( pattern );
    return ( -1 === searchResult ) ? -1 : searchResult + startIndex;
}

String.prototype.regexLastIndexOf = function( pattern, startIndex )
{
    startIndex = startIndex === undefined ? this.length : startIndex;
    var searchResult = this.substr( 0, startIndex ).reverse().regexIndexOf( pattern, 0 );
    return ( -1 === searchResult ) ? -1 : this.length - ++searchResult;
}

String.prototype.reverse = function()
{
    return this.split('').reverse().join('');
}

// Indexes 0123456789
var str = 'caabbccdda';

alert( [
        str.regexIndexOf( /[cd]/, 4 )
    ,   str.regexLastIndexOf( /[cd]/, 4 )
    ,   str.regexIndexOf( /[yz]/, 4 )
    ,   str.regexLastIndexOf( /[yz]/, 4 )
    ,   str.lastIndexOf( 'd', 4 )
    ,   str.regexLastIndexOf( /d/, 4 )
    ,   str.lastIndexOf( 'd' )
    ,   str.regexLastIndexOf( /d/ )
    ]
);

</script>

Je n'ai pas entièrement testé ces méthodes, mais elles semblent fonctionner jusqu'à présent.

0 votes

Mise à jour pour traiter ces cas

0 votes

Chaque fois que je suis sur le point d'accepter cette réponse, je trouve un nouveau cas ! Ceux-ci donnent des résultats différents ! alert( [str.lastIndexOf( /[d]/, 4 ), str.regexLastIndexOf( /[d]/, 4 )]) ;

0 votes

Bien sûr qu'ils le sont - str.lastIndexOf fera une coercition de type sur le motif - en le convertissant en une chaîne de caractères. La chaîne "/[d]/" n'est certainement pas trouvée dans l'entrée, donc le -1 renvoyé est en fait exact.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X