web-dev-qa-db-ja.com

文字列 "StartsWith"が別の文字列かどうかを確認する方法

C#の String.StartsWith と同等のものをJavaScriptで書くにはどうすればよいですか。

var haystack = 'hello world';
var needle = 'he';

haystack.startsWith(needle) == true

注:これは古い質問であり、コメントECMAScript 2015(ES6)で指摘されているように .startsWith メソッドが導入されました。しかし、この更新を書いている時点で(2015) ブラウザのサポートは完全には程遠い

1605
sol

ECMAScript 6の String.prototype.startsWith() メソッドを使用できますが、 すべてのブラウザでまだサポートされているわけではありません 。あなたはそれをサポートしていないブラウザでそれを追加するためにシム/ポリフィルを使用したいと思うでしょう。 仕様に記載されているすべての詳細 に準拠する実装を作成するのは、少し複雑です。あなたが忠実なシムが欲しいならば、どちらかを使ってください:

メソッドをシムしたら(または既にそれを持っているブラウザとJavaScriptエンジンだけをサポートしているのなら)、このようにそれを使うことができます:

"Hello World!".startsWith("He"); // true

var haystack = "Hello world";
var prefix = 'orl';
haystack.startsWith(prefix); // false
1707
CMS

.lastIndexOf を使用したもう1つの方法

haystack.lastIndexOf(needle, 0) === 0

これはhaystackのインデックス0から始まるneedleの出現をhaystackを通して逆方向に調べます。つまり、haystackneedleで始まっているかどうかだけをチェックします。

原則として、これは他のいくつかのアプローチよりもパフォーマンス上の利点があります。

  • haystack全体を検索するわけではありません。
  • 新しい一時的な文字列は作成されず、すぐに破棄されます。
1251
Mark Byers
data.substring(0, input.length) === input
583
cobbal

ヘルパー関数がなければ、正規表現の .test メソッドを使うだけです。

/^He/.test('Hello world')

ハードコードされたものではなく動的な文字列でこれを行うには(文字列に正規表現の制御文字が含まれないと仮定して):

new RegExp('^' + needle).test(haystack)

あなたはチェックアウトするべきです JavascriptにRegExp.escape関数がありますか? 正規表現の制御文字が文字列に現れる可能性がある場合。

182
Vincent

最良の解決策:

function startsWith(str, Word) {
    return str.lastIndexOf(Word, 0) === 0;
}

startsWith("aaa", "a")
true
startsWith("aaa", "ab")
false
startsWith("abc", "abc")
true
startsWith("abc", "c")
false
startsWith("abc", "a")
true
startsWith("abc", "ba")
false
startsWith("abc", "ab")
true

そしてここに endsWith があります。

function endsWith(str, Word) {
    return str.indexOf(Word, str.length - Word.length) !== -1;
}

それをStringにプロトタイプ化することを好む人のために:

String.prototype.startsWith || (String.prototype.startsWith = function(Word) {
    return this.lastIndexOf(Word, 0) === 0;
});

String.prototype.endsWith   || (String.prototype.endsWith = function(Word) {
    return this.indexOf(Word, this.length - Word.length) !== -1;
});

使用法:

"abc".startsWith("ab")
true
"c".ensdWith("c") 
true
53
momomo

私はちょうどこれについて私の意見を加えたいと思いました。

私たちはちょうどこのように使うことができると思います:

var haystack = 'hello world';
var needle = 'he';

if (haystack.indexOf(needle) == 0) {
  // Code if string starts with this substring
}
51
Mr.D

これは、CMSのソリューションに対する小さな改善です。

if(!String.prototype.startsWith){
    String.prototype.startsWith = function (str) {
        return !this.indexOf(str);
    }
}

"Hello World!".startsWith("He"); // true

 var data = "Hello world";
 var input = 'He';
 data.startsWith(input); // true

将来のブラウザでネイティブコードで実装されている場合や、別のライブラリで実装されている場合に、その関数が既に存在するかどうかを確認します。例えば、プロトタイプライブラリはすでにこの機能を実装しています。

!の使用は、=== 0よりもわずかに速くて簡潔ですが、読みやすくはありません。

38
Kikuchyo

underscore.string.js もチェックしてください。 startsWithメソッドを含む、たくさんの便利な文字列テストや操作メソッドが付属しています。ドキュメントから:

startsWith _.startsWith(string, starts)

このメソッドはstringstartsで始まるかどうかをチェックします。

_("image.gif").startsWith("image")
=> true
21
studgeek

私は最近自分自身に同じ質問をしました。
可能な解決策は複数あります。有効な解決策は3つあります。

  • s.indexOf(starter) === 0
  • s.substr(0,starter.length) === starter
  • s.lastIndexOf(starter, 0) === 0(Mark Byersの answer を見た後に追加されました)
  • ループを使う:

    function startsWith(s,starter) {
      for (var i = 0,cur_c; i < starter.length; i++) {
        cur_c = starter[i];
        if (s[i] !== starter[i]) {
          return false;
        }
      }
      return true;
    }
    

私はループを利用する最後の解決策に出会ったことがありません。
驚くべきことに、このソリューションは最初の3つを大幅に上回っています。
これが私がこの結論に達するために行ったjsperfテストです: http://jsperf.com/startswith2/2

平和

ps:ecmascript 6(harmony)では、文字列用のネイティブなstartsWithメソッドが導入されました。
最初のバージョンにこのように必要なメソッドを含めることを考えていたとしたら、どれだけの時間が節約できたかと思います。

更新

Steveが指摘したように(この答えに対する最初のコメント)、与えられたprefixが文字列全体より短い場合、上記のカスタム関数はエラーを投げます。彼はそれを修正し、 http://jsperf.com/startswith2/4 で表示できるループ最適化を追加しました。

Steveが含んでいた2つのループ最適化があることに注意してください、2つのうちの最初のものはより良い性能を示しました、それで私はそのコードを以下に投稿します:

function startsWith2(str, prefix) {
  if (str.length < prefix.length)
    return false;
  for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
    continue;
  return i < 0;
}
15
Raj Nathani

これはとてもポピュラーなので、ECMA 6にこのメソッドの実装があり、それに備えて将来の問題や涙を防ぐために「公式の」ポリフィルを使うべきであることを指摘する価値があると思います。

幸いなことに、Mozillaのエキスパートは私たちに1つを提供してくれます:

https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position) {
        position = position || 0;
        return this.indexOf(searchString, position) === position;
    };
}

これにはECMA 6への移行時に正常に無視されるという利点があります。

11
Scheintod

最善の解決策は、ライブラリ呼び出しの使用をやめて、2つの配列で作業していることを認識することです。手作業による実装は、私がここで見た他のどのソリューションよりも短く、しかも高速です。

function startsWith2(str, prefix) {
    if (str.length < prefix.length)
        return false;
    for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
        continue;
    return i < 0;
}

パフォーマンスの比較(成功と失敗)については、 http://jsperf.com/startswith2/4 を参照してください。 (私の頭を悩ませている可能性のある最新バージョンを必ずチェックしてください。)

5
Steve Hollasch

私はちょうどこの文字列ライブラリについて学びました:

http://stringjs.com/ /

Jsファイルをインクルードしてから、次のようにS変数を使用します。

S('hi there').endsWith('hi there')

インストールすることでNodeJSで使用することもできます。

npm install string

それからS変数としてそれを必要とします:

var S = require('string');

Webページには、代替の文字列ライブラリへのリンクもあります。

2
Ashley Davis
var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
    return true;
else
    return false;
1
Chris
  1. 質問は少し古いですが、ここで提供されるすべての回答とJim Buckが共有するjsperfに基づいて作成したベンチマークを示すために、この回答を書きたいと思いました。

基本的に、長い針が長い干し草の山の中にあり、最後の文字を除いて非常に似ているかどうかをすばやく見つける方法が必要でした。

これは、各関数(splice、substring、startsWithなど)が1.000.0001文字のhaystack文字列(nestedString)と偽りまたは真実の針に対してfalseとtrueの両方を返すときにテストするコードです。 1.000.000文字の文字列(それぞれtestParentStringFalseおよびtestParentStringTrue):

// nestedString is made of 1.000.001 '1' repeated characters.
var nestedString = '...'

// testParentStringFalse is made of 1.000.000 characters,
// all characters are repeated '1', but the last one is '2',
// so for this string the test should return false.
var testParentStringFalse = '...'

// testParentStringTrue is made of 1.000.000 '1' repeated characters,
// so for this string the test should return true.
var testParentStringTrue = '...'

// You can make these very long strings by running the following bash command
// and edit each one as needed in your editor
// (NOTE: on OS X, `pbcopy` copies the string to the clipboard buffer,
//        on Linux, you would probably need to replace it with `xclip`):
// 
//     printf '1%.0s' {1..1000000} | pbcopy
// 

function testString() {
    let dateStart
    let dateEnd
    let avg
    let count = 100000
    const falseResults = []
    const trueResults = []

    /* slice */
    console.log('========> slice')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== slice')
    console.log('')
    /* slice END */

    /* lastIndexOf */
    console.log('========> lastIndexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringFalse, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringTrue, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== lastIndexOf')
    console.log('')
    /* lastIndexOf END */

    /* indexOf */
    console.log('========> indexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringFalse) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringTrue) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== indexOf')
    console.log('')
    /* indexOf END */

    /* substring */
    console.log('========> substring')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== substring')
    console.log('')
    /* substring END */

    /* startsWith */
    console.log('========> startsWith')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringFalse)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringTrue)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== startsWith')
    console.log('')
    /* startsWith END */

    falseResults.sort((a, b) => a.avg - b.avg)
    trueResults.sort((a, b) => a.avg - b.avg)

    console.log('false results from fastest to slowest avg:', falseResults)
    console.log('true results from fastest to slowest avg:', trueResults)
}

Chrome 75Firefox 67Safari 12およびOpera 62でこのベンチマークテストを実行しました。

このマシンにはEdgeとIEがないため、これらは含めませんでしたが、もし誰かがEdgeと少なくともIE 9に対してスクリプトを実行し、出力をここで共有したい場合は、結果を見て非常に興味があります。

3つの長い文字列を再作成し、スクリプトをファイルに保存する必要があることを忘れないでください。ブラウザのコンソールでコピー/貼り付けを行うと、各文字列の長さが1.000.000を超えるため、スクリプトがブロックされます。

出力は次のとおりです。

Chrome 75(substring勝):

false results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08271}
2)  {"label":"slice","avg":0.08615}
3)  {"label":"lastIndexOf","avg":0.77025}
4)  {"label":"indexOf","avg":1.64375}
5)  {"label":"startsWith","avg":3.5454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08213}
2)  {"label":"slice","avg":0.08342}
3)  {"label":"lastIndexOf","avg":0.7831}
4)  {"label":"indexOf","avg":0.88988}
5)  {"label":"startsWith","avg":3.55448}

Firefox 67(indexOfが勝ちます):

false results from fastest to slowest avg
1)  {"label":"indexOf","avg":0.1807}
2)  {"label":"startsWith","avg":0.74621}
3)  {"label":"substring","avg":0.74898}
4)  {"label":"slice","avg":0.78584}
5)  {"label":"lastIndexOf","avg":0.79668}

true results from fastest to slowest avg:
1)  {"label":"indexOf","avg":0.09528}
2)  {"label":"substring","avg":0.75468}
3)  {"label":"startsWith","avg":0.76717}
4)  {"label":"slice","avg":0.77222}
5)  {"label":"lastIndexOf","avg":0.80527}

Safari 12(sliceは偽の結果で勝ち、startsWithは真の結果で勝ちます。また、Safariはテスト全体を実行する合計時間の面で最速です):

false results from fastest to slowest avg:
1) "{\"label\":\"slice\",\"avg\":0.0362}"
2) "{\"label\":\"startsWith\",\"avg\":0.1141}"
3) "{\"label\":\"lastIndexOf\",\"avg\":0.11512}"
4) "{\"label\":\"substring\",\"avg\":0.14751}"
5) "{\"label\":\"indexOf\",\"avg\":0.23109}"

true results from fastest to slowest avg:
1) "{\"label\":\"startsWith\",\"avg\":0.11207}"
2) "{\"label\":\"lastIndexOf\",\"avg\":0.12196}"
3) "{\"label\":\"substring\",\"avg\":0.12495}"
4) "{\"label\":\"indexOf\",\"avg\":0.33667}"
5) "{\"label\":\"slice\",\"avg\":0.49923}"

Opera 62(substringが勝ちます。結果はChromeに似ており、OperaはChromiumとBlinkに基づいているので驚きません):

false results from fastest to slowest avg:
{"label":"substring","avg":0.09321}
{"label":"slice","avg":0.09463}
{"label":"lastIndexOf","avg":0.95347}
{"label":"indexOf","avg":1.6337}
{"label":"startsWith","avg":3.61454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08855}
2)  {"label":"slice","avg":0.12227}
3)  {"label":"indexOf","avg":0.79914}
4)  {"label":"lastIndexOf","avg":1.05086}
5)  {"label":"startsWith","avg":3.70808}

すべてのブラウザーには独自の実装の詳細があります(ChromeのChromiumとBlinkに基づくOperaを除く)。

もちろん、さまざまなユースケースでさらにテストを実行することができ、実行する必要があります(たとえば、haystackと比較してneedleが本当に短い場合、haystackがneedleより短い場合など)。しかし、私の場合、非常に長い文字列とここで共有したかった。

1
tonix

私はjavascriptについてはわかりませんが、TypeScriptでは次のようなことをしました

var str = "something";
(<String>str).startsWith("some");

Jsでも動作するはずです。私はそれが役立つことを願っています!

0

ここでの答えに基づいて、JSPerfテストに基づいて最高のパフォーマンスが得られるように(そして私が言える限り機能的には完成しているので)、これは私が現在使用しているバージョンです。

if(typeof String.prototype.startsWith != 'function'){
    String.prototype.startsWith = function(str){
        if(str == null) return false;
        var i = str.length;
        if(this.length < i) return false;
        for(--i; (i >= 0) && (this[i] === str[i]); --i) continue;
        return i < 0;
    }
}

これはここからのstartsWith2に基づいていました: http://jsperf.com/startswith2/6 。小さなパフォーマンスの向上のために小さなTweakを追加し、それ以来、比較文字列がnullまたは未定義であることのチェックも追加し、CMSの回答の手法を使用してStringプロトタイプに追加するように変換しました。

この実装はこの Mozilla Developer Network のページで言及されている "position"パラメータをサポートしていませんが、それでもECMAScriptの提案の一部ではないようです。

0
Edward Millen