Esiste una versione di JavaScript String.indexOf () che consente espressioni regolari?

In javascript, esiste un equivalente di String.indexOf () che prende un’espressione regolare invece di una stringa per il primo parametro mentre continua a consentire un secondo parametro?

Devo fare qualcosa del genere

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

e

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

Mentre String.search () prende un espressione regolare come parametro, non mi consente di specificare un secondo argomento!

Modificare:
Ciò si è rivelato più difficile di quanto pensassi all’inizio, quindi ho scritto una piccola funzione di test per testare tutte le soluzioni fornite … presume che regexIndexOf e regexLastIndexOf siano stati aggiunti all’object 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)]) ; } } 

e sto testando come segue per assicurarmi che almeno per un carattere regexp, il risultato è lo stesso come se usassimo indexOf

// Cerca l’a tra i xe
Test ( ‘xxx’);
Test ( ‘axx’);
Test ( ‘xax’);
Test ( ‘xxa’);
Test ( ‘AXA’);
Test ( ‘xaa’);
Test ( ‘aax’);
Test ( ‘aaa’);

Combinando alcuni degli approcci già menzionati (indexOf è ovviamente piuttosto semplice), penso che queste siano le funzioni che faranno il trucco:

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

Ovviamente, la modifica dell'object String incorporato invierà bandiere rosse per la maggior parte delle persone, ma questa potrebbe essere una volta in cui non è un grosso problema; semplicemente sii consapevole di ciò


AGGIORNAMENTO: Modificato regexLastIndexOf() modo che ora sembra imitare lastIndexOf() . Per favore fatemi sapere se fallisce ancora e in quali circostanze.


AGGIORNARE: passa tutti i test trovati nei commenti su questa pagina e il mio. Certo, questo non significa che sia a prova di proiettile. Qualsiasi feedback apprezzato.

Le istanze del costruttore String hanno un metodo .search() che accetta un RegExp e restituisce l’indice della prima corrispondenza.

Per avviare la ricerca da una posizione particolare (simulando il secondo parametro di .indexOf() ) puoi .indexOf() i primi caratteri i :

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

Ma questo otterrà l’indice nella stringa più breve (dopo che la prima parte è stata tagliata), quindi dovrai aggiungere la lunghezza della parte tagliata ( i ) all’indice restituito se non fosse -1 . Questo ti darà l’indice nella stringa originale:

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

Ho una versione breve per te. Funziona bene per me!

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

E se vuoi una versione prototipo:

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

EDIT : se si desidera aggiungere il supporto per 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; } 

Per usarlo, semplice come questo:

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

Uso:

 str.search(regex) 

Vedi la documentazione qui.

Potresti usare substr.

 str.substr(i).match(/[abc]/); 

Basato sulla risposta di BaileyP. La differenza principale è che questi metodi restituiscono -1 se il modello non può essere abbinato.

Edit: Grazie alla risposta di Jason Bunting ho avuto un’idea. Perché non modificare la proprietà .lastIndex della regex? Anche se questo funzionerà solo per i modelli con la bandiera globale ( /g ).

Modifica: aggiornato per passare i casi di prova.

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

Non è nativo, ma è certamente ansible aggiungere questa funzionalità

  

Non ho testato completamente questi metodi, ma sembrano funzionare così lontano.

RexExp istanze di RexExp hanno già una proprietà lastIndex (se sono globali) e quindi quello che sto facendo è copiare l’espressione regolare, modificandola leggermente per adattarla ai nostri scopi, eseguendola sulla stringa e osservando l’ lastIndex . Questo sarà inevitabilmente più veloce del loop sulla stringa. (Hai abbastanza esempi su come inserirlo nel prototipo di stringa, giusto?)

 function reIndexOf(reIn, str, startIndex) { var re = new RegExp(reIn.source, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; function reLastIndexOf(reIn, str, startIndex) { var src = /\$$/.test(reIn.source) && !/\\\$$/.test(reIn.source) ? reIn.source : reIn.source + '(?![\\S\\s]*' + reIn.source + ')'; var re = new RegExp(src, 'g' + (reIn.ignoreCase ? 'i' : '') + (reIn.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; reIndexOf(/[abc]/, "tommy can eat"); // Returns 6 reIndexOf(/[abc]/, "tommy can eat", 8); // Returns 11 reLastIndexOf(/[abc]/, "tommy can eat"); // Returns 11 

È anche ansible prototipare le funzioni sull’object RegExp:

 RegExp.prototype.indexOf = function(str, startIndex) { var re = new RegExp(this.source, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; RegExp.prototype.lastIndexOf = function(str, startIndex) { var src = /\$$/.test(this.source) && !/\\\$$/.test(this.source) ? this.source : this.source + '(?![\\S\\s]*' + this.source + ')'; var re = new RegExp(src, 'g' + (this.ignoreCase ? 'i' : '') + (this.multiLine ? 'm' : '')); re.lastIndex = startIndex || 0; var res = re.exec(str); if(!res) return -1; return re.lastIndex - res[0].length; }; /[abc]/.indexOf("tommy can eat"); // Returns 6 /[abc]/.indexOf("tommy can eat", 8); // Returns 11 /[abc]/.lastIndexOf("tommy can eat"); // Returns 11 

Una rapida spiegazione di come sto modificando RegExp : Per indexOf devo solo assicurarmi che il flag globale sia impostato. Per lastIndexOf di I sto usando un look-ahead negativo per trovare l’ultima occorrenza a meno che il RegExp fosse già in corrispondenza alla fine della stringa.

Dopo che tutte le soluzioni proposte hanno fallito i miei test in un modo o nell’altro, (edit: alcuni sono stati aggiornati per superare i test dopo averlo scritto) Ho trovato l’implementazione di mozilla per Array.indexOf e Array.lastIndexOf

Ho usato quelli per implementare la mia versione di String.prototype.regexIndexOf e String.prototype.regexLastIndexOf come segue:

 String.prototype.regexIndexOf = function(elt /*, from*/) { var arr = this.split(''); var len = arr.length; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; for (; from < len; from++) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; }; String.prototype.regexLastIndexOf = function(elt /*, from*/) { var arr = this.split(''); var len = arr.length; var from = Number(arguments[1]); if (isNaN(from)) { from = len - 1; } else { from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) from += len; else if (from >= len) from = len - 1; } for (; from > -1; from--) { if (from in arr && elt.exec(arr[from]) ) return from; } return -1; }; 

Sembrano passare le funzioni di test che ho fornito nella domanda.

Ovviamente funzionano solo se l’espressione regolare corrisponde a un carattere ma questo è sufficiente per il mio scopo dato che lo userò per cose come ([abc], \ s, \ W, \ D)

Continuerò a monitorare la domanda nel caso qualcuno offra un’implementazione migliore / più veloce / più pulita / più generica che funzioni su qualsiasi espressione regolare.

Avevo bisogno di una funzione regexIndexOf anche per un array, quindi ne ho programmato uno anch’io. Tuttavia dubito che sia ottimizzato, ma suppongo che dovrebbe funzionare correttamente.

 Array.prototype.regexIndexOf = function (regex, startpos = 0) { len = this.length; for(x = startpos; x < len; x++){ if(typeof this[x] != 'undefined' && (''+this[x]).match(regex)){ return x; } } return -1; } arr = []; arr.push(null); arr.push(NaN); arr[3] = 7; arr.push('asdf'); arr.push('qwer'); arr.push(9); arr.push('...'); console.log(arr); arr.regexIndexOf(/\d/, 4); 

In alcuni casi semplici, puoi semplificare la ricerca all’indietro usando split.

 function regexlast(string,re){ var tokens=string.split(re); return (tokens.length>1)?(string.length-tokens[tokens.length-1].length):null; } 

Questo ha alcuni problemi seri:

  1. le partite sovrapposte non verranno visualizzate
  2. l’indice restituito è per la fine della partita piuttosto che all’inizio (bene se la tua regex è una costante)

Ma dal lato positivo è molto meno codice. Per un’espressione regolare a lunghezza costante che non può sovrapporsi (come /\s\w/ per trovare i limiti delle parole) questo è abbastanza buono.

Per i dati con corrispondenze sparse, l’uso di string.search è il più veloce tra i browser. Ridisegna una stringa ogni iterazione per:

 function lastIndexOfSearch(string, regex, index) { if(index === 0 || index) string = string.slice(0, Math.max(0,index)); var idx; var offset = -1; while ((idx = string.search(regex)) !== -1) { offset += idx + 1; string = string.slice(idx + 1); } return offset; } 

Per i dati densi ho fatto questo. È complesso rispetto al metodo di esecuzione, ma per i dati densi, è 2-10 volte più veloce di ogni altro metodo che ho provato e circa 100 volte più veloce della soluzione accettata. I punti principali sono:

  1. Chiama exec sulla regex passata una volta per verificare che ci sia una corrispondenza o uscire presto. Lo faccio usando (? = In un metodo simile, ma su IE il controllo con exec è molto più veloce.
  2. Costruisce e memorizza nella cache una regex modificata nel formato ‘(r). (?!. ? r) ‘
  3. La nuova regex viene eseguita e vengono restituiti i risultati di quell’esecut o del primo exec;

     function lastIndexOfGroupSimple(string, regex, index) { if (index === 0 || index) string = string.slice(0, Math.max(0, index + 1)); regex.lastIndex = 0; var lastRegex, index flags = 'g' + (regex.multiline ? 'm' : '') + (regex.ignoreCase ? 'i' : ''), key = regex.source + '$' + flags, match = regex.exec(string); if (!match) return -1; if (lastIndexOfGroupSimple.cache === undefined) lastIndexOfGroupSimple.cache = {}; lastRegex = lastIndexOfGroupSimple.cache[key]; if (!lastRegex) lastIndexOfGroupSimple.cache[key] = lastRegex = new RegExp('.*(' + regex.source + ')(?!.*?' + regex.source + ')', flags); index = match.index; lastRegex.lastIndex = match.index; return (match = lastRegex.exec(string)) ? lastRegex.lastIndex - match[1].length : index; }; 

jsPerf dei metodi

Non capisco lo scopo dei test in cima. Le situazioni che richiedono una regex sono impossibili da confrontare con una chiamata a indexOf, che credo sia il punto di fare il metodo in primo luogo. Per far passare il test, ha più senso usare ‘xxx + (?! x)’, piuttosto che regolare il modo in cui la regex itera.

L’ultimo indice di Jason Bunting non funziona. Il mio non è ottimale, ma funziona.

 //Jason Bunting's String.prototype.regexIndexOf = function(regex, startpos) { var indexOf = this.substring(startpos || 0).search(regex); return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf; } String.prototype.regexLastIndexOf = function(regex, startpos) { var lastIndex = -1; var index = this.regexIndexOf( regex ); startpos = startpos === undefined ? this.length : startpos; while ( index >= 0 && index < startpos ) { lastIndex = index; index = this.regexIndexOf( regex, index + 1 ); } return lastIndex; } 

Non ci sono ancora metodi nativi che eseguono l’attività richiesta.

Ecco il codice che sto usando. Mima il comportamento dei metodi String.prototype.indexOf e String.prototype.lastIndexOf , ma accetta anche un RegExp come argomento di ricerca oltre a una stringa che rappresenta il valore da cercare.

Sì, è abbastanza lungo perché una risposta va cercando di seguire gli standard attuali il più vicino ansible e, naturalmente, contiene una quantità ragionevole di commenti JSDOC . Tuttavia, una volta minified, il codice è solo 2.27k e una volta gzip per la trasmissione è solo 1023 byte.

I 2 metodi che questo aggiunge a String.prototype (usando Object.defineProperty dove disponibile) sono:

  1. searchOf
  2. searchLastOf

Supera tutti i test che l’OP ha pubblicato e inoltre ho testato le routine in modo abbastanza approfondito nel mio uso quotidiano e ho cercato di essere sicuro che funzionino in più ambienti, ma i feedback / problemi sono sempre benvenuti.

 /*jslint maxlen:80, browser:true */ /* * Properties used by searchOf and searchLastOf implementation. */ /*property MAX_SAFE_INTEGER, abs, add, apply, call, configurable, defineProperty, enumerable, exec, floor, global, hasOwnProperty, ignoreCase, index, lastIndex, lastIndexOf, length, max, min, multiline, pow, prototype, remove, replace, searchLastOf, searchOf, source, toString, value, writable */ /* * Properties used in the testing of searchOf and searchLastOf implimentation. */ /*property appendChild, createTextNode, getElementById, indexOf, lastIndexOf, length, searchLastOf, searchOf, unshift */ (function () { 'use strict'; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1, getNativeFlags = new RegExp('\\/([az]*)$', 'i'), clipDups = new RegExp('([\\s\\S])(?=[\\s\\S]*\\1)', 'g'), pToString = Object.prototype.toString, pHasOwn = Object.prototype.hasOwnProperty, stringTagRegExp; /** * Defines a new property directly on an object, or modifies an existing * property on an object, and returns the object. * * @private * @function * @param {Object} object * @param {string} property * @param {Object} descriptor * @returns {Object} * @see https://goo.gl/CZnEqg */ function $defineProperty(object, property, descriptor) { if (Object.defineProperty) { Object.defineProperty(object, property, descriptor); } else { object[property] = descriptor.value; } return object; } /** * Returns true if the operands are strictly equal with no type conversion. * * @private * @function * @param {*} a * @param {*} b * @returns {boolean} * @see http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4 */ function $strictEqual(a, b) { return a === b; } /** * Returns true if the operand inputArg is undefined. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isUndefined(inputArg) { return $strictEqual(typeof inputArg, 'undefined'); } /** * Provides a string representation of the supplied object in the form * "[object type]", where type is the object type. * * @private * @function * @param {*} inputArg The object for which a class string represntation * is required. * @returns {string} A string value of the form "[object type]". * @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2 */ function $toStringTag(inputArg) { var val; if (inputArg === null) { val = '[object Null]'; } else if ($isUndefined(inputArg)) { val = '[object Undefined]'; } else { val = pToString.call(inputArg); } return val; } /** * The string tag representation of a RegExp object. * * @private * @type {string} */ stringTagRegExp = $toStringTag(getNativeFlags); /** * Returns true if the operand inputArg is a RegExp. * * @private * @function * @param {*} inputArg * @returns {boolean} */ function $isRegExp(inputArg) { return $toStringTag(inputArg) === stringTagRegExp && pHasOwn.call(inputArg, 'ignoreCase') && typeof inputArg.ignoreCase === 'boolean' && pHasOwn.call(inputArg, 'global') && typeof inputArg.global === 'boolean' && pHasOwn.call(inputArg, 'multiline') && typeof inputArg.multiline === 'boolean' && pHasOwn.call(inputArg, 'source') && typeof inputArg.source === 'string'; } /** * The abstract operation throws an error if its argument is a value that * cannot be converted to an Object, otherwise returns the argument. * * @private * @function * @param {*} inputArg The object to be tested. * @throws {TypeError} If inputArg is null or undefined. * @returns {*} The inputArg if coercible. * @see https://goo.gl/5GcmVq */ function $requireObjectCoercible(inputArg) { var errStr; if (inputArg === null || $isUndefined(inputArg)) { errStr = 'Cannot convert argument to object: ' + inputArg; throw new TypeError(errStr); } return inputArg; } /** * The abstract operation converts its argument to a value of type string * * @private * @function * @param {*} inputArg * @returns {string} * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tostring */ function $toString(inputArg) { var type, val; if (inputArg === null) { val = 'null'; } else { type = typeof inputArg; if (type === 'string') { val = inputArg; } else if (type === 'undefined') { val = type; } else { if (type === 'symbol') { throw new TypeError('Cannot convert symbol to string'); } val = String(inputArg); } } return val; } /** * Returns a string only if the arguments is coercible otherwise throws an * error. * * @private * @function * @param {*} inputArg * @throws {TypeError} If inputArg is null or undefined. * @returns {string} */ function $onlyCoercibleToString(inputArg) { return $toString($requireObjectCoercible(inputArg)); } /** * The function evaluates the passed value and converts it to an integer. * * @private * @function * @param {*} inputArg The object to be converted to an integer. * @returns {number} If the target value is NaN, null or undefined, 0 is * returned. If the target value is false, 0 is returned * and if true, 1 is returned. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4 */ function $toInteger(inputArg) { var number = +inputArg, val = 0; if ($strictEqual(number, number)) { if (!number || number === Infinity || number === -Infinity) { val = number; } else { val = (number > 0 || -1) * Math.floor(Math.abs(number)); } } return val; } /** * Copies a regex object. Allows adding and removing native flags while * copying the regex. * * @private * @function * @param {RegExp} regex Regex to copy. * @param {Object} [options] Allows specifying native flags to add or * remove while copying the regex. * @returns {RegExp} Copy of the provided regex, possibly with modified * flags. */ function $copyRegExp(regex, options) { var flags, opts, rx; if (options !== null && typeof options === 'object') { opts = options; } else { opts = {}; } // Get native flags in use flags = getNativeFlags.exec($toString(regex))[1]; flags = $onlyCoercibleToString(flags); if (opts.add) { flags += opts.add; flags = flags.replace(clipDups, ''); } if (opts.remove) { // Would need to escape `options.remove` if this was public rx = new RegExp('[' + opts.remove + ']+', 'g'); flags = flags.replace(rx, ''); } return new RegExp(regex.source, flags); } /** * The abstract operation ToLength converts its argument to an integer * suitable for use as the length of an array-like object. * * @private * @function * @param {*} inputArg The object to be converted to a length. * @returns {number} If len <= +0 then +0 else if len is +INFINITY then * 2^53-1 else min(len, 2^53-1). * @see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength */ function $toLength(inputArg) { return Math.min(Math.max($toInteger(inputArg), 0), MAX_SAFE_INTEGER); } /** * Copies a regex object so that it is suitable for use with searchOf and * searchLastOf methods. * * @private * @function * @param {RegExp} regex Regex to copy. * @returns {RegExp} */ function $toSearchRegExp(regex) { return $copyRegExp(regex, { add: 'g', remove: 'y' }); } /** * Returns true if the operand inputArg is a member of one of the types * Undefined, Null, Boolean, Number, Symbol, or String. * * @private * @function * @param {*} inputArg * @returns {boolean} * @see https://goo.gl/W68ywJ * @see https://goo.gl/ev7881 */ function $isPrimitive(inputArg) { var type = typeof inputArg; return type === 'undefined' || inputArg === null || type === 'boolean' || type === 'string' || type === 'number' || type === 'symbol'; } /** * The abstract operation converts its argument to a value of type Object * but fixes some environment bugs. * * @private * @function * @param {*} inputArg The argument to be converted to an object. * @throws {TypeError} If inputArg is not coercible to an object. * @returns {Object} Value of inputArg as type Object. * @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.9 */ function $toObject(inputArg) { var object; if ($isPrimitive($requireObjectCoercible(inputArg))) { object = Object(inputArg); } else { object = inputArg; } return object; } /** * Converts a single argument that is an array-like object or list (eg. * arguments, NodeList, DOMTokenList (used by classList), NamedNodeMap * (used by attributes property)) into a new Array() and returns it. * This is a partial implementation of the ES6 Array.from * * @private * @function * @param {Object} arrayLike * @returns {Array} */ function $toArray(arrayLike) { var object = $toObject(arrayLike), length = $toLength(object.length), array = [], index = 0; array.length = length; while (index < length) { array[index] = object[index]; index += 1; } return array; } if (!String.prototype.searchOf) { /** * This method returns the index within the calling String object of * the first occurrence of the specified value, starting the search at * fromIndex. Returns -1 if the value is not found. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] The location within the calling string * to start the search from. It can be any * integer. The default value is 0. If * fromIndex < 0 the entire string is * searched (same as passing 0). If * fromIndex >= str.length, the method will * return -1 unless searchValue is an empty * string in which case str.length is * returned. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, match, rx; if (!$isRegExp(regex)) { return String.prototype.indexOf.apply(str, args); } if ($toLength(args.length) > 1) { fromIndex = +args[1]; if (fromIndex < 0) { fromIndex = 0; } } else { fromIndex = 0; } if (fromIndex >= $toLength(str.length)) { return result; } rx = $toSearchRegExp(regex); rx.lastIndex = fromIndex; match = rx.exec(str); if (match) { result = +match.index; } return result; } }); } if (!String.prototype.searchLastOf) { /** * This method returns the index within the calling String object of * the last occurrence of the specified value, or -1 if not found. * The calling string is searched backward, starting at fromIndex. * * @function * @this {string} * @param {RegExp|string} regex A regular expression object or a String. * Anything else is implicitly converted to * a String. * @param {Number} [fromIndex] Optional. The location within the * calling string to start the search at, * indexed from left to right. It can be * any integer. The default value is * str.length. If it is negative, it is * treated as 0. If fromIndex > str.length, * fromIndex is treated as str.length. * @returns {Number} If successful, returns the index of the first * match of the regular expression inside the * string. Otherwise, it returns -1. */ $defineProperty(String.prototype, 'searchLastOf', { enumerable: false, configurable: true, writable: true, value: function (regex) { var str = $onlyCoercibleToString(this), args = $toArray(arguments), result = -1, fromIndex, length, match, pos, rx; if (!$isRegExp(regex)) { return String.prototype.lastIndexOf.apply(str, args); } length = $toLength(str.length); if (!$strictEqual(args[1], args[1])) { fromIndex = length; } else { if ($toLength(args.length) > 1) { fromIndex = $toInteger(args[1]); } else { fromIndex = length - 1; } } if (fromIndex >= 0) { fromIndex = Math.min(fromIndex, length - 1); } else { fromIndex = length - Math.abs(fromIndex); } pos = 0; rx = $toSearchRegExp(regex); while (pos <= fromIndex) { rx.lastIndex = pos; match = rx.exec(str); if (!match) { break; } pos = +match.index; if (pos <= fromIndex) { result = pos; } pos += 1; } return result; } }); } }()); (function () { 'use strict'; /* * testing as follow to make sure that at least for one character regexp, * the result is the same as if we used indexOf */ var pre = document.getElementById('out'); function log(result) { pre.appendChild(document.createTextNode(result + '\n')); } function test(str) { var i = str.length + 2, r, a, b; while (i) { a = str.indexOf('a', i); b = str.searchOf(/a/, i); r = ['Failed', 'searchOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); a = str.lastIndexOf('a', i); b = str.searchLastOf(/a/, i); r = ['Failed', 'searchLastOf', str, i, a, b]; if (a === b) { r[0] = 'Passed'; } log(r); i -= 1; } } /* * Look for the a among the xes */ test('xxx'); test('axx'); test('xax'); test('xxa'); test('axa'); test('xaa'); test('aax'); test('aaa'); }()); 
 

Bene, poiché stai solo cercando di far corrispondere la posizione di un personaggio , l’espressione regolare è probabilmente eccessiva.

Presumo che tutto ciò che vuoi sia, invece di “trovare prima di tutto questo personaggio”, trova il primo di questi personaggi.

Questa è ovviamente la risposta semplice, ma fa quello che la tua domanda si propone di fare, anche se senza la parte regex (perché non hai chiarito perché specificatamente doveva essere una regex)

 function mIndexOf( str , chars, offset ) { var first = -1; for( var i = 0; i < chars.length; i++ ) { var p = str.indexOf( chars[i] , offset ); if( p < first || first === -1 ) { first = p; } } return first; } String.prototype.mIndexOf = function( chars, offset ) { return mIndexOf( this, chars, offset ); # I'm really averse to monkey patching. }; mIndexOf( "hello world", ['a','o','w'], 0 ); >> 4 mIndexOf( "hello world", ['a'], 0 ); >> -1 mIndexOf( "hello world", ['a','o','w'], 4 ); >> 4 mIndexOf( "hello world", ['a','o','w'], 5 ); >> 6 mIndexOf( "hello world", ['a','o','w'], 7 ); >> -1 mIndexOf( "hello world", ['a','o','w','d'], 7 ); >> 10 mIndexOf( "hello world", ['a','o','w','d'], 10 ); >> 10 mIndexOf( "hello world", ['a','o','w','d'], 11 ); >> -1