JavaScript'in String.indexOf () yönteminin düzenli ifadelere izin veren bir sürümü var mı?


214

Javascript, hala ikinci parametreye izin verirken ilk ilk parametre için bir dize yerine normal bir ifade alan String.indexOf () eşdeğeri var mı?

Gibi bir şey yapmam gerek

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

ve

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

String.search () bir regexp'yi parametre olarak alırken, ikinci bir argüman belirtmeme izin vermiyor!

Düzenleme:
Bu aslında tüm çözümleri test etmek için küçük bir test fonksiyonu yazdım ben aslında düşündüğümden daha zor olduğu ortaya çıktı ... regexIndexOf ve regexLastIndexOf String nesnesine eklendi varsayar.

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

ve en azından bir karakter regexp için sonucun indexOf kullandığımızla aynı olduğundan emin olmak için aşağıdaki şekilde test ediyorum

// xes
testi ('xxx') arasında a arayın ;
Test ( 'axx');
Test ( 'xax');
Test ( 'XXa');
Test ( 'AXA');
Test ( 'Xaa');
Test ( 'aax');
Test ( 'AAA');


|içeride [ ]değişmez karakterle eşleşir |. Muhtemelen demek istedin [abc].
Markus Jarderot

Evet teşekkürler haklısın, düzelteceğim ama regexp kendisi ilgisiz ...
Pat

Cevabım güncellendi Pat, herhangi bir geri bildirim için teşekkürler.
Jason Bunting

Daha basit ve etkili bir yaklaşım sadece string.match (/ [AZ] /) kullanmak olduğunu buldum. Çok fazla yoksa, yöntem null değerini döndürür, aksi takdirde bir nesne alırsınız, ilk büyük harfin dizinini almak için match (/ [AZ] /). Dizinini yapabilirsiniz
Syler

Yanıtlar:


129

Daha önce bahsedilen yaklaşımlardan birkaçını birleştirerek (indexOf açıkçası oldukça basittir), bunların hile yapacak işlevler olduğunu düşünüyorum:

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

Açıkçası, yerleşik String nesnesini değiştirmek çoğu insan için kırmızı bayraklar gönderir, ancak bu bir anlaşma o kadar büyük olmadığında bir kez olabilir; sadece farkında olun.


GÜNCELLEME: Düzenlendi, regexLastIndexOf()böylece lastIndexOf()şimdi taklit gibi görünüyor . Lütfen hala başarısız olup olmadığını ve hangi koşullarda başarısız olduğunu bildirin.


GÜNCELLEME: Bu sayfadaki yorumlarda ve kendiminkinde bulunan tüm testleri geçer. Tabii ki, bu kurşun geçirmez olduğu anlamına gelmez. Herhangi bir geri bildirim takdir.


Kişisel regexLastIndexOfirade sadece son dizinini döndürür örtüşmeyen maçı.
Markus Jarderot

Üzgünüm, BÜYÜK bir regex adam değil - bana benimkini başarısız kılacak bir örnek verebilir misin? Daha fazla bilgi edinebildiğiniz için teşekkür ederim, ancak yanıtınız benim kadar cahil birine yardımcı olmaz. :)
Jason Bunting

Jason Soruyu test etmek için biraz fonksiyon ekledim. bu başarısız olur (diğer testlerin yanı sıra) aşağıdaki 'axx'.lastIndexOf (' a ', 2)! =' axx'.regexLastIndexOf (/ a /, 2)
Pat

2
Bunun kullanmak daha verimli olduğunu düşünüyorum regex.lastIndex = result.index + 1;yerine regex.lastIndex = ++nextStop;. Herhangi bir sonuç kaybetmeden umarım bir sonraki maça çok daha hızlı ilerler.
Gedrox

1
Eğer npm'den çekmeyi tercih ediyorsanız, bu iki util fonksiyon şu anda NPM'de
Capaj

185

StringYapıcı örnekleri, RegExp'i kabul eden ve ilk eşleşmenin dizinini döndüren bir .search()yönteme sahiptir.

Aramayı belirli bir konumdan başlatmak için (ikinci parametresini taklit ederek .indexOf()) sliceilk ikarakterleri kapatabilirsiniz :

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

Ancak bu, dizini daha kısa dizede alır (ilk bölüm dilimlendikten sonra), bu idurumda kesilmemiş bölümün ( ) uzunluğunu döndürülmemiş dizine eklemek istersiniz -1. Bu size orijinal dizedeki dizini verecektir:

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

1
sorudan: String.search () parametre olarak bir regexp alırken, ikinci bir argüman belirtmeme izin vermiyor!
Pat

14
str.substr (i) .search (/ re /)
Glenn

6
Harika bir çözüm, ancak çıktı biraz farklı. indexOf, başlangıçtan itibaren bir sayı döndürür (ofsetten bağımsız olarak), ancak bu, ofsetten konumu döndürür. Parite için, bunun gibi bir şey daha isteyeceksiniz:function regexIndexOf(text, offset) { var initial = text.substr(offset).search(/re/); if(initial >= 0) { initial += offset; } return initial; }
gkoberger

39

Senin için kısa bir versiyonum var. Benim için iyi çalışıyor!

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

Ve bir prototip sürümü istiyorsanız:

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 : fromIndex için destek eklemek istiyorsanız

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

Kullanmak için, bu kadar basit:

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

Bu aslında güzel bir numara. startIndexParametreyi her zamanki gibi almak indeoxOfve lastIndexOfyapmak için genişletirseniz harika olur .
Robert Koritnik

@RobertKoritnik - Cevabımı startIndex(veya fromIndex) destekledim . Umarım yardımcı olur!
pmrotule

lastIndexOfRegexfromIndexsonucun değerini de geri eklemelidir .
Peter

Aşağıdaki senaryoda algoritmanız bozulacaktır: "aRomeo Romeo".indexOfRegex(new RegExp("\\bromeo", 'gi'));Sonuç 7 olması gerektiğinde 1 olacaktır, çünkü indexOf bir sözcüğün başında olsun ya da olmasın ilk kez "romeo" görünecektir.
KorelK

13

kullanın:

str.search(regex)

Buradaki belgelere bakın .


11
@OZZIE: Hayır, pek değil. Temelde Glenn'in cevabı (~ 150 upvotes ile), ancak hiçbir açıklaması yok, başlangıç ​​pozisyonunu desteklemiyor0 ve yedi yıl sonra yayınlandı .
ccjmne

7

BaileyP'nin cevabına dayanarak. Temel fark, -1örüntü eşleştirilemezse bu yöntemlerin geri dönmesidir.

Edit: Jason Bunting'in cevabı sayesinde bir fikrim var. Neden .lastIndexnormal ifadenin özelliğini değiştirmiyoruz ? Rağmen bu sadece küresel bayrağı ( /g) olan desenler için çalışır .

Düzenleme: Test senaryolarını geçmek için güncellendi.

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

Bu şimdiye kadar en umut verici görünüyor (birkaç sytax düzeltmesinden sonra) :-) Sadece kenar koşullarında birkaç test başarısız. 'Axx'.lastIndexOf (' a ', 0)! =' Axx'.regexLastIndexOf (/ a /, 0) gibi şeyler ... Bu durumları düzeltebileceğimi görmek için içine bakıyorum
Pat

6

Substr kullanabilirsiniz.

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

O'Reilly tarafından yayınlanan iyi bilinen JavaScript kitabından: "substr, ECMAScript tarafından standartlaştırılmamıştır ve bu nedenle kullanımdan kaldırılmıştır." Ama ne elde ettiğinin ardındaki temel fikri seviyorum.
Jason Bunting

1
Bu bir sorun değil. GERÇEKTEN bu konuda endişeliyseniz, bunun yerine String.substring () kullanın - sadece biraz farklı matematik yapmak zorunda. Ayrıca, JavaScript ana diline% 100 eklenmemelidir.
Peter Bailey

Bu bir sorun değil - kodunuzu, ECMAScript standartlarına uymak istedikleri için substr uygulamayan bir uygulamaya karşı çalıştırırsanız, sorun yaşayacaksınız. Verilmiş, onu alt dize ile değiştirmek yapmak o kadar zor değil, ama bunun farkında olmak iyi.
Jason Bunting

1
Sorunlarınız olduğu anda çok çok basit çözümleriniz var. Bence yorumlar mantıklı, ama aşağı oylama bilgiçlikti.
VoronoiPotato

Çalışan bir demo kodu sağlamak için cevabınızı düzenleyebilir misiniz?
vsync

5

RexExpörnekler zaten bir lastIndex özelliğine sahiptir (eğer küreselse ) ve bu yüzden yaptığım şey normal ifadeyi kopyalamak, amaçlarımıza uyacak şekilde biraz değiştirmek exec, dizede-yapmak ve bakmaktır lastIndex. Bu, kaçınılmaz olarak ip üzerinde döngüden daha hızlı olacaktır. (Bunu dize prototipine nasıl koyacağınıza dair yeterli örneğiniz var, değil mi?)

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

Ayrıca işlevleri RegExp nesnesine de prototipleyebilirsiniz:

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

Nasıl değiştirdiğime dair kısa bir açıklama RegExp: Çünkü indexOfsadece global bayrağın ayarlandığından emin olmalıyım. Çünkü lastIndexOfben RegExpzaten dizenin sonunda eşleşen sürece son oluşumu bulmak için bir negatif ileriye bakıyorum .


4

Yerel olarak değil, ancak kesinlikle bu işlevi ekleyebilirsiniz

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

Bu yöntemleri tam olarak test etmedim, ancak şimdiye kadar çalışıyor gibi görünüyorlar.


Bu davaları ele almak için güncellendi
Peter Bailey

everytime ben bu yanıtı kabul etmek üzereyim yeni bir dava bulmak! Bunlar farklı sonuçlar verir! alert ([str.lastIndexOf (/ [d] /, 4), str.regexLastIndexOf (/ [d] /, 4)]);
Pat

tabii, onlar - str.lastIndexOf desen üzerinde baskı yapacak - bir dizeye dönüştürecek. Girişte "/ [d] /" dizesi kesinlikle bulunamadığından, döndürülen -1 aslında doğrudur.
Peter Bailey

Anladım. String.lastIndexOf () üzerinde spec okuduktan sonra - sadece bu argümanın nasıl çalıştığını yanlış anladım. Bu yeni sürüm işlemeli.
Peter Bailey

Bir şey hala doğru değil, ama geç oluyor ... Bir test davası almaya çalışacağım ve belki de sabahları düzelteceğim. Şimdilik sorun için özür dilerim.
Pat

2

Önerilen tüm çözümler testlerimi bir şekilde başarısız hale getirdikten sonra, (edit: bazıları bunu yazdıktan sonra testleri geçmek için güncellendi) Array.indexOf ve Array.lastIndexOf için mozilla uygulamasını buldum

Bu benim String.prototype.regexIndexOf ve String.prototype.regexLastIndexOf benim sürümünü uygulamak için aşağıdaki gibi kullanılır:

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

Soruda verdiğim test fonksiyonlarını geçiyor gibi görünüyorlar.

Açıkçası sadece normal ifade bir karakterle eşleşirse çalışırlar, ancak bu benim amacım için yeterlidir çünkü ([abc], \ s, \ W, \ D) gibi şeyler için kullanacağım

Birisinin herhangi bir normal ifade üzerinde çalışan daha iyi / daha hızlı / daha temiz / daha genel bir uygulama sağlaması durumunda soruyu izlemeye devam edeceğim.


Vay canına, bu uzun bir kod. Lütfen güncellenmiş cevabımı kontrol edin ve geri bildirim sağlayın. Teşekkürler.
Jason Bunting

Bu uygulama, Firefox ve SpiderMonkey JavaScript motorundaki lastIndexOf ile tartışmalı bir şekilde son durumlar da dahil olmak üzere mutlak uyumluluğu amaçlamaktadır. [...] gerçek dünyadaki uygulamalarda, bu durumları görmezden gelirseniz, daha az karmaşık kodlarla hesaplayabilirsiniz.
Pat

Mozilla sayfası oluşturun :-) Ben sadece tüm kenar durumlarda bırakarak kod reklam değişikliği iki satır aldı. Diğer cevapların birkaçı testleri geçmek için güncellendiğinden, bunları karşılaştırmayı deneyeceğim ve en etkili olanı kabul edeceğim. Sorunu tekrar gözden geçirmek için zamanım olduğunda.
Patenti

Çözümümü güncelledim ve başarısız olmasına neden olan geri bildirimleri veya şeyleri takdir ediyorum. MizardX tarafından işaret edilen örtüşen sorunu düzeltmek için bir değişiklik yaptım (umarım!)
Jason Bunting

2

regexIndexOfBir dizi için de bir fonksiyona ihtiyacım vardı , bu yüzden kendim bir programladım. Ancak, bunun optimize edildiğinden şüpheliyim, ancak sanırım düzgün çalışmalı.

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

1

Bazı basit durumlarda, bölme özelliğini kullanarak geriye doğru aramanızı basitleştirebilirsiniz.

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

Bunun birkaç ciddi sorunu var:

  1. çakışan eşleşmeler görünmez
  2. döndürülen endeks, başlangıçtan ziyade maçın sonu içindir (normal ifadeniz sabitse iyi)

Ama parlak tarafta daha az kod var. Üst üste binemeyen sabit uzunluklu bir regex /\s\w/için ( kelime sınırlarını bulmak gibi ) bu yeterince iyidir.


0

Seyrek eşleşmeleri olan veriler için string.search kullanmak tarayıcılar arasında en hızlısıdır. Her yinelemeyi bir dizeye yeniden dilimler:

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

Yoğun veriler için bunu yaptım. Yürütme yöntemine kıyasla karmaşıktır, ancak yoğun veriler için, denediğim diğer tüm yöntemlerden 2-10 kat daha hızlı ve kabul edilen çözümden yaklaşık 100 kat daha hızlıdır. Ana noktalar:

  1. Bir eşleşme olduğunu doğrulamak veya erken çıkmak için bir kez iletilen normal ifade üzerinde exec çağırır. Bunu kullanarak (? = Benzer bir yöntemde yapıyorum ama exec ile IE üzerinde kontrol çok daha hızlı.
  2. '(R) biçiminde değiştirilmiş bir normal ifadeyi oluşturur ve önbelleğe alır. (?!. ? r) '
  3. Yeni normal ifade yürütülür ve bu exec veya ilk exec'den alınan sonuçlar döndürülür;

    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 yöntemleri

Testlerin amacını anlayamıyorum. Bir regex gerektiren durumlar, ilk etapta yöntem yapma noktası olduğunu düşünüyorum indexOf bir çağrı ile karşılaştırmak mümkün değildir. Testi geçmek için, normal ifadenin yinelenme şeklini ayarlamak yerine 'xxx + (?! x)' kullanmak daha mantıklıdır.


0

Jason Bunting'in son dizini çalışmıyor. Benimki uygun değil ama işe yarıyor.

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

Benimkinin başarısız olmasına neden olan bir test yapabilir misiniz? Bunun işe yaramadığını fark ettiyseniz, bir test durumu sağlayın, neden "işe yaramadı" deyin ve yerinde optimal olmayan bir çözüm sağlayın?
Jason Bunting

Hoo çocuk. Tamamen haklısın. Bir örnek vermeliydim. Ne yazık ki aylar önce bu koddan devam ettim ve başarısız vakanın ne olduğu hakkında hiçbir fikrim yok. : - /
Eli

şey, hayat böyle. :)
Jason Bunting

0

İstenen görevi gerçekleştiren yerel yöntemler hala yok.

İşte kullandığım kod. String.prototype.indexOf ve String.prototype.lastIndexOf yöntemlerinin davranışlarını taklit eder, ancak aranacak değeri temsil eden bir dizeye ek olarak bir RegExp öğesini arama bağımsız değişkeni olarak da kabul ederler.

Evet, mevcut standartları olabildiğince yakın takip etmeye çalıştığından ve elbette makul miktarda JSDOC yorumu içerdiğinden, bir yanıt gittikçe oldukça uzundur . Ancak, küçültüldükten sonra kod yalnızca 2.27k ve iletim için sıkıştırıldıktan sonra sadece 1023 bayttır.

Bunun eklediği 2 yöntem String.prototype(varsa, Object.defineProperty kullanılarak):

  1. searchOf
  2. searchLastOf

OP'nin yayınladığı tüm testleri geçer ve ayrıca günlük kullanımımda rutinleri tamamen test ettim ve birden fazla ortamda çalıştıklarından emin olmaya çalıştım, ancak geri bildirim / sorunlar her zaman memnuniyetle karşılandı.

/*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('\\/([a-z]*)$', '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');
}());
<pre id="out"></pre>


0

RegExp ile çok basit bir lastIndex araması arıyorsanız ve lastIndexOf'u son ayrıntıya benzetip takmadığını umursamıyorsanız, bu dikkatinizi çekebilir.

Ben sadece dize ters, ve ilk oluşum dizini uzunluğundan - 1 çıkarın. Bu benim test geçmek olur, ama bence uzun dizeleri ile bir performans sorunu ortaya çıkabilir.

interface String {
  reverse(): string;
  lastIndex(regex: RegExp): number;
}

String.prototype.reverse = function(this: string) {
  return this.split("")
    .reverse()
    .join("");
};

String.prototype.lastIndex = function(this: string, regex: RegExp) {
  const exec = regex.exec(this.reverse());
  return exec === null ? -1 : this.length - 1 - exec.index;
};

0

String.prototype.match(regex)Dize verilen tüm eşleşen eşleşmeler dizesi döndürür kullanılan regex(daha fazla bilgi, buraya bakın ):

function getLastIndex(text, regex, limit = text.length) {
  const matches = text.match(regex);

  // no matches found
  if (!matches) {
    return -1;
  }

  // matches found but first index greater than limit
  if (text.indexOf(matches[0] + matches[0].length) > limit) {
    return -1;
  }

  // reduce index until smaller than limit
  let i = matches.length - 1;
  let index = text.lastIndexOf(matches[i]);
  while (index > limit && i >= 0) {
    i--;
    index = text.lastIndexOf(matches[i]);
  }
  return index > limit ? -1 : index;
}

// expect -1 as first index === 14
console.log(getLastIndex('First Sentence. Last Sentence. Unfinished', /\. /g, 10));

// expect 29
console.log(getLastIndex('First Sentence. Last Sentence. Unfinished', /\. /g));


0
var mystring = "abc ab a";
var re  = new RegExp("ab"); // any regex here

if ( re.exec(mystring) != null ){ 
   alert("matches"); // true in this case
}

Standart düzenli ifadeler kullanın:

var re  = new RegExp("^ab");  // At front
var re  = new RegExp("ab$");  // At end
var re  = new RegExp("ab(c|d)");  // abc or abd

-2

Sadece bir karakterin pozisyonuna uymaya çalıştığınız için , normal ifade muhtemelen aşırıdır.

Sanırım istediğin tek şey, "bu karakterin ilkini bul" yerine, bu karakterlerden ilkini bulmak.

Bu elbette basit bir cevaptır, ancak regex kısmı olmasa da sorunuzun yapmak istediği şeyi yapar (çünkü neden özel olarak regex olması gerektiğini netleştirmediniz)

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

Maymun yamasıyla ilgili bir yorum - sorunlarının farkında olduğum halde - küresel isim alanını kirletmenin daha iyi olduğunu mu düşünüyorsun? Her iki durumda da sembol çatışmaları olamaz gibi bir sorun ortaya çıkar ve temelde aynı şekilde yeniden düzenlenir / onarılır.
Peter Bailey

Eh \ s ve bazı durumlarda \ W aramak gerekir ve tüm olasılıkları numaralandırmak zorunda değildi umuyordu.
Pat

BaileyP: küresel ad alanı kirliliği olmadan bu sorunu çözebilirsiniz, örneğin: örneğin jQuery'ye bakın. bu modeli kullanın. proje için bir nesne, eşyalarınız onun içine girer. Mootooller ağzımda kötü bir tat bıraktı.
Kent Fredric

Ayrıca ben orada yazdığı gibi asla kod unutmayın. örnek, kullanım senaryosu nedenleriyle basitleştirilmiştir.
Kent Fredric
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.