web-dev-qa-db-fra.com

Comment vérifier si une chaîne "StartsWith" est une autre chaîne?

Comment pourrais-je écrire l'équivalent de C # String.StartsWith en JavaScript?

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

haystack.startsWith(needle) == true
_

Remarque: Cette question est ancienne et, comme indiqué dans les commentaires, ECMAScript 2015 (ES6) a introduit la méthode .startsWith . Cependant, au moment de la rédaction de cette mise à jour (2015) le support du navigateur est loin d'être complet .

1636
sol

Vous pouvez utiliser la méthode String.prototype.startsWith() d'ECMAScript 6, mais c'est pas encore prise en charge par tous les navigateurs . Vous voudrez utiliser un shim/polyfill pour l'ajouter aux navigateurs qui ne le prennent pas en charge. Créer une implémentation conforme à tous les détails de la spécification est un peu compliqué. Si vous voulez une cale fidèle, utilisez soit:

Une fois que vous avez sélectionné la méthode (ou si vous ne supportez que les navigateurs et les moteurs JavaScript qui en disposent déjà), vous pouvez l'utiliser comme ceci:

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

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

Une autre alternative avec .lastIndexOf :

_haystack.lastIndexOf(needle, 0) === 0
_

Ceci recherche en arrière dans haystack une occurrence de needle à partir de l'index _0_ de haystack. En d'autres termes, il vérifie uniquement si haystack commence par needle.

En principe, cela devrait présenter des avantages en termes de performances par rapport à d'autres approches:

  • Il ne recherche pas dans tout le haystack.
  • Il ne crée pas de nouvelle chaîne temporaire et ne la supprime pas immédiatement.
1262
Mark Byers
data.substring(0, input.length) === input
586
cobbal

Sans fonction d'assistance, il suffit d'utiliser la méthode .test == de =:

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

Pour ce faire, utilisez une chaîne dynamique plutôt que codée en dur (en supposant que la chaîne ne contiendra aucun caractère de contrôle d'expression rationnelle):

_new RegExp('^' + needle).test(haystack)
_

Vous devriez vérifier Y a-t-il une fonction RegExp.escape dans Javascript? s'il existe la possibilité que des caractères de contrôle d'expression rationnelle apparaissent dans la chaîne.

182
Vincent

meilleure solution:

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

Utilisé:

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

Et voici endsWith si vous en avez aussi besoin:

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

Pour ceux qui préfèrent le prototyper dans 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;
});

tilisation:

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

Je voulais juste ajouter mon opinion à ce sujet.

Je pense que nous pouvons simplement utiliser comme ceci:

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

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

Voici une amélioration mineure à la solution de 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

Vérifier si la fonction existe déjà si un futur navigateur l'implémente en code natif ou si elle est implémentée par une autre bibliothèque. Par exemple, la bibliothèque de prototypes implémente déjà cette fonction.

L'utilisation de ! est légèrement plus rapide et concise que === 0 bien qu'elle ne soit pas aussi lisible.

38
Kikuchyo

Consultez également nderscore.string.js . Il contient de nombreuses méthodes de test et de manipulation de chaînes utiles, notamment une méthode startsWith. De la docs:

startsWith _.startsWith(string, starts)

Cette méthode vérifie si string commence par starts.

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

Je me suis récemment posé la même question.
Il existe de nombreuses solutions possibles, voici 3 solutions valables:

  • s.indexOf(starter) === 0
  • s.substr(0,starter.length) === starter
  • s.lastIndexOf(starter, 0) === 0 (ajouté après avoir vu les réponse de Mark Byers)
  • en utilisant une boucle:

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

Je n'ai pas rencontré la dernière solution qui utilise une boucle.
Étonnamment, cette solution surpasse de loin les 3 premiers par une marge significative.
Voici le test jsperf que j’ai effectué pour parvenir à cette conclusion: http://jsperf.com/startswith2/2

Paix

ps: ecmascript 6 (harmonie) introduit une méthode native startsWith pour les chaînes.
Imaginez combien de temps aurait été économisé s’ils avaient eu l’intention d’inclure cette méthode indispensable dans la version initiale.

Mettre à jour

Comme Steve l'a souligné (premier commentaire de cette réponse), la fonction personnalisée ci-dessus renvoie une erreur si le préfixe indiqué est plus court que la chaîne entière. Il a corrigé cela et ajouté une optimisation de boucle qui peut être consultée à l'adresse suivante: http://jsperf.com/startswith2/4 .

Notez qu'il y a 2 optimisations de boucle que Steve a incluses, le premier des deux a montré de meilleures performances, donc je posterai ce code ci-dessous:

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

Comme cela est si populaire, je pense qu’il est utile de souligner qu’il existe une implémentation de cette méthode dans ECMA 6 et que, pour ce faire, il convient d’utiliser le polyfill "officiel" afin de prévenir les problèmes futurs et les déchirures.

Heureusement, les experts de Mozilla nous en fournissent un:

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

Veuillez noter que cela présente l’avantage d’être ignoré gracieusement lors de la transition vers ECMA 6.

11
Scheintod

La solution la plus performante consiste à cesser d'utiliser les appels de bibliothèque et à reconnaître que vous travaillez avec deux baies. Une implémentation manuelle est à la fois courte et plus rapide que toutes les autres solutions que j'ai vues ici.

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

Pour des comparaisons de performances (succès et échecs), voir http://jsperf.com/startswith2/4 . (Assurez-vous de vérifier les versions ultérieures qui ont pu remplacer la mienne.)

5
Steve Hollasch

Je viens d'apprendre à propos de cette bibliothèque de chaînes:

http://stringjs.com/

Incluez le fichier js puis utilisez la variable S comme ceci:

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

Il peut également être utilisé dans NodeJS en l’installant:

npm install string

Ensuite, en tant que variable S:

var S = require('string');

La page Web contient également des liens vers d'autres bibliothèques de chaînes, si celle-ci ne vous convient pas.

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. La question est un peu ancienne, mais je voulais écrire cette réponse pour vous montrer quelques points de repère que j'ai établis sur la base de toutes les réponses fournies ici et du jsperf partagé par Jim Buck.

Il me fallait essentiellement un moyen rapide de déterminer si une longue aiguille se trouvait dans une longue botte de foin et si elles étaient très similaires, à l'exception des derniers caractères.

Voici le code que j'ai écrit qui, pour chaque fonction (épissure, sous-chaîne, débutant avec, etc.) teste les deux quand ils renvoient false et true contre une chaîne de botte de foin (nestedString) de 1.000.0001 caractères et une aiguille de fausseté ou de vérité chaîne de 1.000.000 caractères (testParentStringFalse et testParentStringTrue, respectivement):

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

J'ai exécuté ce test de référence sur Chrome 75, Firefox 67, Safari 12 et Opera 62.

Je n'ai pas inclus Edge et IE car je ne les ai pas sur cette machine, mais si quelqu'un d'entre vous veut exécuter le script contre Edge et au moins IE 9 et partager le résultat ici, je le ferais. soyez très curieux de voir les résultats.

Rappelez-vous simplement que vous devez recréer les 3 longues chaînes et enregistrer le script dans un fichier que vous ouvrez ensuite dans votre navigateur. Un copier/coller sur la console du navigateur le bloquera car la longueur de chaque chaîne est> = 1.000.000).

Voici les sorties:

Chrome 75 (substring gagne):

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

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 gagne pour de faux résultats, startsWith gagne pour de vrais résultats, également Safari est le plus rapide en termes de temps total pour exécuter l'ensemble du test):

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 gagne. Les résultats sont similaires à Chrome et je ne suis pas surpris car Opera est basé sur Chromium et 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}

Il s'avère que chaque navigateur a ses propres détails d'implémentation (à part Opera, qui est basé sur Chrome et Blink de Chrome).

Bien sûr, des tests supplémentaires avec différents cas d'utilisation pourraient et devraient être effectués (par exemple, lorsque l'aiguille est vraiment courte comparée à la botte de foin, lorsque la botte de foin est plus courte que l'aiguille, etc.), mais dans mon cas, j'avais besoin de comparer de très longues chaînes et je voulais le partager ici.

1
tonix

Je ne suis pas sûr pour le javascript, mais dans TypeScript j'ai fait quelque chose comme

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

Je suppose que cela devrait fonctionner aussi sur js. J'espère que ça aide!

0
Andreas Hadjithoma

Sur la base des réponses fournies ici, voici la version que j’utilise maintenant, car elle semble offrir les meilleures performances basées sur les tests JSPerf (et est fonctionnellement complète, à ce que je sache).

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

Ceci était basé sur startsWith2 à partir d'ici: http://jsperf.com/startswith2/6 . J'ai ajouté un petit tweak pour une amélioration minime des performances, et depuis lors, j'ai également vérifié la chaîne de comparaison comme étant nulle ou indéfinie, et l'a convertie pour l'ajouter au prototype de chaîne en utilisant la technique de la réponse du CMS.

Notez que cette implémentation ne supporte pas le paramètre "position" qui est mentionné dans cette page Mozilla Developer Network , mais cela ne semble de toute façon pas faire partie de la proposition ECMAScript.

0
Edward Millen