1802 Stimmen

Wie prüft man, ob eine Zeichenkette "mit" einer anderen Zeichenkette beginnt?

Wie würde ich das Äquivalent zu C#s String.StartsWith in JavaScript?

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

haystack.startsWith(needle) == true

Hinweis: Dies ist eine alte Frage, und wie in den Kommentaren erwähnt, führte ECMAScript 2015 (ES6) die .startsWith Methode. Zum Zeitpunkt der Erstellung dieser Aktualisierung (2015) Die Browserunterstützung ist noch lange nicht vollständig .

5voto

Steve Hollasch Punkte 1863

Die performanteste Lösung ist, keine Bibliotheksaufrufe mehr zu verwenden und einfach zu erkennen, dass man mit zwei Arrays arbeitet. Eine handgesteuerte Implementierung ist sowohl kurz als auch schneller als jede andere Lösung, die ich hier gesehen habe.

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

Für Leistungsvergleiche (Erfolg und Misserfolg), siehe http://jsperf.com/startswith2/4 . (Prüfen Sie, ob es spätere Versionen gibt, die meine übertrumpft haben könnten).

3voto

tonix Punkte 6284
  1. Die Frage ist schon etwas älter, aber ich wollte diese Antwort schreiben, um Ihnen einige Benchmarks zu zeigen, die ich auf der Grundlage aller hier gegebenen Antworten und des von Jim Buck veröffentlichten jsperf erstellt habe.

Ich brauchte eine schnelle Methode, um herauszufinden, ob sich eine lange Nadel in einem langen Heuhaufen befindet, und sie sind sich bis auf die letzten Zeichen sehr ähnlich.

Hier ist der Code, den ich geschrieben habe und der für jede Funktion (splice, substring, startsWith, etc.) testet, ob sie false oder true zurückgibt, anhand einer Heuhaufen-Zeichenkette ( nestedString ) von 1.000.0001 Zeichen und eine falsche oder wahre Nadelfolge von 1.000.000 Zeichen ( testParentStringFalse y testParentStringTrue bzw.):

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

Ich habe diesen Benchmark-Test unter folgenden Bedingungen durchgeführt Chrom 75 , Firefox 67 , Safari 12 y Oper 62 .

Ich habe Edge und IE nicht einbezogen, weil ich sie nicht auf diesem Rechner habe, aber wenn jemand von euch das Skript gegen Edge und zumindest IE 9 ausführen und die Ausgabe hier teilen möchte, wäre ich sehr neugierig auf die Ergebnisse.

Denken Sie nur daran, dass Sie die 3 langen Strings neu erstellen und das Skript in einer Datei speichern müssen, die Sie dann in Ihrem Browser öffnen, da die Konsole des Browsers beim Kopieren/Einfügen blockiert wird, da die Länge jedes Strings >= 1.000.000 ist).

Hier sind die Ergebnisse:

Chrom 75 ( substring gewinnt):

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 gewinnt):

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 gewinnt für falsche Ergebnisse, startsWith gewinnt für echte Ergebnisse, außerdem ist Safari der schnellste in Bezug auf die Gesamtzeit für die Ausführung des gesamten Tests):

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

Oper 62 ( substring gewinnt. Die Ergebnisse sind ähnlich wie bei Chrome und das überrascht mich nicht, da Opera auf Chromium und Blink basiert):

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}

Es stellt sich heraus, dass jeder Browser seine eigenen Implementierungsdetails hat (mit Ausnahme von Opera, das auf Chromium und Blink von Chrome basiert).

Natürlich könnten und sollten weitere Tests mit verschiedenen Anwendungsfällen durchgeführt werden (z.B. wenn die Nadel im Vergleich zum Heuhaufen wirklich kurz ist, wenn der Heuhaufen kürzer als die Nadel ist, usw.), aber in meinem Fall musste ich sehr lange Strings vergleichen und wollte dies hier mitteilen.

2voto

Ashley Davis Punkte 9489

Ich habe gerade von dieser String-Bibliothek erfahren:

http://stringjs.com/

Binden Sie die js-Datei ein und verwenden Sie dann die S Variable wie diese:

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

Es kann auch in NodeJS verwendet werden, indem es installiert wird:

npm install string

Dann benötigen Sie es als S variabel:

var S = require('string');

Die Webseite enthält auch Links zu alternativen String-Bibliotheken, falls Ihnen diese nicht zusagt.

1voto

Chris Punkte 1263
var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
    return true;
else
    return false;

0voto

Edward Millen Punkte 144

Auf der Grundlage der Antworten hier ist dies die Version, die ich jetzt verwende, da sie nach den JSPerf-Tests die beste Leistung zu bieten scheint (und funktional vollständig ist, soweit ich das beurteilen kann).

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

Dies basierte auf startsWith2 von hier: http://jsperf.com/startswith2/6 . Ich fügte eine kleine Optimierung für eine winzige Leistungsverbesserung hinzu und habe seitdem auch eine Prüfung für die Vergleichszeichenfolge hinzugefügt, die null oder undefiniert ist, und konvertierte es in den String-Prototyp mit der Technik in CMS Antwort hinzufügen.

Beachten Sie, dass diese Implementierung den Parameter "Position" nicht unterstützt, der in diesem Dokument erwähnt wird. Mozilla-Entwickler-Netzwerk Seite, aber das scheint ohnehin nicht Teil des ECMAScript-Vorschlags zu sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X