250

JavaScript では、2 番目のパラメータを許可しながら最初の最初のパラメータに文字列の代わりに正規表現を取る String.indexOf() に相当するものはありますか?

私は何かをする必要があります

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

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

String.search() はパラメーターとして正規表現を受け取りますが、2 番目の引数を指定することはできません!

編集:
これは当初考えていたよりも難しいことが判明したため、提供されたすべてのソリューションをテストする小さなテスト関数を作成しました... regexIndexOf と regexLastIndexOf が 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)]) ;
    }
}

そして、少なくとも1文字の正規表現について、結果がindexOfを使用した場合と同じであることを確認するために、次のようにテストしています

//xesの中からaを探す
test('xxx');
テスト ('axx');
テスト ('xax');
テスト ('xxa');
テスト('アクサ');
テスト ('xaa');
テスト ('aax');
テスト ('aaa');

4

20 に答える 20

211

Stringコンストラクターのインスタンスには、RegExp を受け取り、最初に一致したインデックスを返す.search()メソッドがあります。

特定の位置から検索を開始するには ( の 2 番目のパラメーターを偽装)、最初の文字をオフにする.indexOf()ことができます。slicei

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

ただし、これは短い文字列のインデックスを取得するため (最初の部分が切り落とされた後)、切り捨てられた部分 ( i) の長さを返されたインデックスに追加する必要があり-1ます。これにより、元の文字列のインデックスが得られます。

function regexIndexOf(text, re, i) {
    var indexInSuffix = text.slice(i).search(re);
    return indexInSuffix < 0 ? indexInSuffix : indexInSuffix + i;
}
于 2008-11-07T22:11:48.880 に答える
150

すでに述べたアプローチのいくつかを組み合わせて (indexOf は明らかにかなり単純です)、これらの関数でうまくいくと思います。

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

更新: 編集regexLastIndexOf()されたため、現在は模倣されているようlastIndexOf()です。それでも失敗する場合は、どのような状況であるかをお知らせください。


更新: このページのコメントで見つかったすべてのテストと、私自身のテストに合格します。もちろん防弾というわけではありません。フィードバックをいただければ幸いです。

于 2008-11-08T00:33:07.833 に答える
7

substr を使用できます。

str.substr(i).match(/[abc]/);
于 2008-11-07T22:07:09.203 に答える
7

BaileyP の回答に基づいています。-1主な違いは、パターンが一致しない場合にこれらのメソッドが返されることです。

編集: Jason Bunting の回答のおかげで、私はアイデアを得ました。.lastIndex正規表現のプロパティを変更しないのはなぜですか? ただし、これはグローバル フラグ ( /g) を持つパターンでのみ機能します。

編集:テストケースに合格するように更新されました。

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;
}
于 2008-11-07T22:48:54.250 に答える
5

RexExpインスタンスにはすでにlastIndexプロパティがあるため (グローバルの場合)、正規表現をコピーし、目的に合わせて少し変更しexecて、文字列に適用し、lastIndex. これは、文字列をループするよりも必然的に高速になります。(これを文字列プロトタイプに配置する方法の例は十分にありますよね?)

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

関数のプロトタイプを 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

の変更方法の簡単な説明RegExp:indexOfグローバル フラグが設定されていることを確認する必要があるだけです。の場合、文字列の最後で既に一致していlastIndexOfない限り、最後の出現を見つけるために否定先読みを使用しています。RegExp

于 2008-11-08T15:52:44.277 に答える
4

ネイティブではありませんが、確かにこの機能を追加できます

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

これらの方法を完全にテストしたわけではありませんが、今のところ機能しているようです。

于 2008-11-07T22:23:59.793 に答える
3

配列にも関数が必要だったregexIndexOfので、自分でプログラムしました。ただし、最適化されているとは思えませんが、適切に機能するはずです。

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);
于 2012-09-01T15:11:01.120 に答える
2

提案されたすべてのソリューションがいずれかの方法でテストに失敗した後 (編集: これを書いた後、いくつかはテストに合格するように更新されました)、Array.indexOfおよびArray.lastIndexOfの mozilla 実装を見つけました。

これらを使用して、私のバージョンの String.prototype.regexIndexOf と String.prototype.regexLastIndexOf を次のように実装しました。

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

質問で提供したテスト関数に合格したようです。

明らかに、正規表現が1文字に一致する場合にのみ機能しますが、( [abc] 、 \s 、 \W 、 \D )のようなものに使用するため、私の目的には十分です。

誰かが正規表現で動作する、より優れた/高速/クリーン/より一般的な実装を提供する場合に備えて、質問を監視し続けます。

于 2008-11-08T12:46:43.217 に答える
0

一致が少ないデータの場合は、string.search を使用するのがどのブラウザーでも最速です。反復ごとに文字列を次のように再スライスします。

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

高密度データの場合、これを作成しました。execute メソッドと比べると複雑ですが、密度の高いデータの場合、私が試した他のすべてのメソッドよりも 2 倍から 10 倍高速であり、受け入れられたソリューションよりも約 100 倍高速です。主なポイントは次のとおりです。

  1. 一度渡された正規表現で exec を呼び出して、一致があるかどうかを確認するか、早期に終了します。同様の方法で (?= を使用してこれを行いますが、IE では exec でチェックする方が劇的に高速です。
  2. '(r) の形式で変更された正規表現を構築してキャッシュします。(?!. ?r)'
  3. 新しい正規表現が実行され、その exec または最初の 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

上のテストの目的がわかりません。正規表現を必要とする状況は、indexOf の呼び出しと比較することは不可能です。これが、最初にメソッドを作成するポイントだと思います。テストに合格するには、正規表現の反復方法を調整するよりも、'xxx+(?!x)' を使用する方が理にかなっています。

于 2014-04-20T02:19:38.947 に答える
0

Jason Bunting の最後のインデックスが機能しません。私のは最適ではありませんが、機能します。

//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;
}
于 2015-06-11T22:47:19.867 に答える
-2

まあ、文字の位置を一致させようとしているだけなので、正規表現はおそらくやり過ぎです。

あなたが望むのは、「これらの文字の最初を見つける」の代わりに、これらの文字の最初を見つけることだけだと思います。

もちろん、これは簡単な答えですが、正規表現の部分がなくても、質問が意図していることを行います(具体的に正規表現でなければならない理由を明確にしていないため)

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
于 2008-11-07T23:10:30.493 に答える