Como verificar se uma string “StartSwith” outra string?
-
22-07-2019 - |
Pergunta
Como eu escreveria o equivalente a C#'s String.StartsWith
em JavaScript?
var haystack = 'hello world';
var needle = 'he';
haystack.startsWith(needle) == true
Nota: Esta é uma pergunta antiga e, como apontado nos comentários, o Ecmascript 2015 (ES6) introduziu o .startsWith
método. No entanto, no momento da redação desta atualização (2015) O suporte do navegador está longe de ser completo.
Solução
Você pode usar o ecmascript 6's String.prototype.startsWith()
método, mas é ainda não suportado em todos os navegadores. Você vai querer usar um Shim/Polyfill para adicioná -lo aos navegadores que não o suportam. Criando uma implementação que cumpra Todos os detalhes estabelecidos na especificação é um pouco complicado. Se você quer um calço fiel, use:
- Matthias Bynens
String.prototype.startsWith
calço, ou - o es6-shim, que calcula o máximo possível da especificação ES6, incluindo
String.prototype.startsWith
.
Depois de interromper o método (ou se você está suportando apenas navegadores e motores JavaScript que já o possuem), você pode usá -lo assim:
"Hello World!".startsWith("He"); // true
var haystack = "Hello world";
var prefix = 'orl';
haystack.startsWith(prefix); // false
Outras dicas
Outra alternativa com .lastIndexOf
:
haystack.lastIndexOf(needle, 0) === 0
Isso olha para trás haystack
para uma ocorrência de needle
a partir do índice 0
do haystack
. Em outras palavras, ele só verifica se haystack
começa com needle
.
Em princípio, isso deve ter vantagens de desempenho sobre algumas outras abordagens:
- Não procura o inteiro
haystack
. - Ele não cria uma nova string temporária e a descarte imediatamente.
data.substring(0, input.length) === input
Sem uma função auxiliar, apenas usando o Regex's .test
método:
/^He/.test('Hello world')
Para fazer isso com uma string dinâmica em vez de um codificado (supondo que a string não contenha nenhum caractere de controle regexp):
new RegExp('^' + needle).test(haystack)
Você deve conferir Existe uma função regexp.escape em JavaScript? Se existir a possibilidade de que os caracteres de controle regexp apareçam na string.
Melhor solução:
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
E aqui está termina com Se você precisar disso também:
function endsWith(str, word) {
return str.indexOf(word, str.length - word.length) !== -1;
}
Para aqueles que preferem protótipo -o na 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;
});
Uso:
"abc".startsWith("ab")
true
"c".ensdWith("c")
true
Eu só queria adicionar minha opinião sobre isso.
Eu acho que podemos usar assim:
var haystack = 'hello world';
var needle = 'he';
if (haystack.indexOf(needle) == 0) {
// Code if string starts with this substring
}
Aqui está uma pequena melhoria da solução do 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
Verificando se a função já existe caso um navegador futuro a implemente no código nativo ou se seja implementado por outra biblioteca. Por exemplo, a biblioteca do protótipo já implementa essa função.
Usando !
é um pouco mais rápido e mais conciso do que === 0
Embora não seja tão legível.
Verifique também subscore.string.js. Ele vem com um monte de métodos úteis de teste de string e manipulação, incluindo um startsWith
método. Dos documentos:
começa com
_.startsWith(string, starts)
Este método verifica se
string
começa comstarts
._("image.gif").startsWith("image") => true
Recentemente, me fiz a mesma pergunta.
Existem várias soluções possíveis, aqui estão três válidas:
s.indexOf(starter) === 0
s.substr(0,starter.length) === starter
s.lastIndexOf(starter, 0) === 0
(Adicionado depois de ver Mark Byers responda)Usando um loop:
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; }
Não encontrei a última solução que utiliza um loop.
Surpreendentemente, essa solução supera os 3 primeiros por uma margem significativa.
Aqui está o teste JSPERF que realizei para chegar a esta conclusão: http://jsperf.com/startswith2/2
Paz
PS: Ecmascript 6 (Harmony) apresenta um nativo startsWith
Método para cordas.
Pense em quanto tempo teria sido salvo se eles pensassem em incluir esse método tão necessário na própria versão inicial.
Atualizar
Como Steve apontou (o primeiro comentário sobre esta resposta), a função personalizada acima lançará um erro se o dado prefixo é mais curto que a corda inteira. Ele consertou isso e adicionou uma otimização de loop que pode ser vista em http://jsperf.com/startswith2/4.
Observe que existem 2 otimizações de loop que Steve incluiu, o primeiro dos dois mostrou melhor desempenho, portanto, postarei esse código abaixo:
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;
}
Como isso é tão popular, acho que vale a pena apontar que existe uma implementação para esse método na ECMA 6 e em preparação para isso deve usar o poli -preenchimento 'oficial' para evitar problemas e lágrimas futuras.
Felizmente, os especialistas da Mozilla nos fornecem um:
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;
};
}
Observe que isso tem a vantagem de ser graciosamente ignorado na transição para a ECMA 6.
A melhor solução de desempenho é parar de usar chamadas de biblioteca e apenas reconhecer que você está trabalhando com duas matrizes. Uma implementação enrolada à mão é curta e também mais rápida que todas as outras soluções que eu já vi aqui.
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;
}
Para comparações de desempenho (sucesso e fracasso), veja http://jsperf.com/startswith2/4. (Certifique -se de verificar versões posteriores que podem ter superado as minhas.)
Acabei de aprender sobre esta biblioteca de string:
Inclua o arquivo JS e depois use o S
variável como esta:
S('hi there').endsWith('hi there')
Também pode ser usado no NodeJS instalando -o:
npm install string
Então exigindo isso como o S
variável:
var S = require('string');
A página da web também possui links para bibliotecas de string alternativas, se essa não gosta.
var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
return true;
else
return false;
Com base nas respostas aqui, esta é a versão que estou usando agora, pois parece fornecer o melhor desempenho com base nos testes JSPERF (e está funcionalmente completo até onde eu sei).
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;
}
}
Isso foi baseado no startSwith2 daqui: http://jsperf.com/startswith2/6. Adicionei um pequeno ajuste para uma pequena melhoria de desempenho e, desde então, também adicionei uma verificação para a string de comparação nula ou indefinida e a converti para adicionar ao protótipo da string usando a técnica na resposta do CMS.
Observe que esta implementação não suporta o parâmetro "posição" mencionado neste Rede de desenvolvedores Mozilla Página, mas isso não parece fazer parte da proposta do ECMAScript de qualquer maneira.
Não tenho certeza para o JavaScript, mas no TypeScript eu fiz algo como
var str = "something";
(<String>str).startsWith("some");
Eu acho que deve funcionar no JS também. Espero que ajude!
- A pergunta é um pouco antiga, mas eu queria escrever esta resposta para mostrar alguns benchmarks que fiz com base em todas as respostas fornecidas aqui e no JSPERF compartilhado por Jim Buck.
Eu basicamente precisava de uma maneira rápida de descobrir se uma agulha longa está dentro de um palheiro longo e eles são muito semelhantes, exceto pelos últimos caracteres.
Aqui está o código que escrevi que para cada função (emenda, substring, startSwith etc.) testes quando eles retornam falsos e verdadeiros contra uma string haystack (nestedString
) de 1.000.0001 caracteres e uma corda de agulha falsamente ou verdadeira de 1.000.000 chars (testParentStringFalse
e testParentStringTrue
, respectivamente):
// 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)
}
Eu executei este teste de referência em Chrome 75, Firefox 67, Safari 12 e Opera 62.
Não incluí o Edge e o IE porque não os tenho nesta máquina, mas se alguém quiser executar o script contra o Edge e pelo menos IE 9 e compartilhar a saída aqui, ficaria muito curioso para ver os resultados.
Lembre -se de que você precisa recriar as três cordas longas e salvar o script em um arquivo que você abre no seu navegador como copiar/colar no console do navegador o bloqueará, pois o comprimento de cada sequência é> = 1.000.000).
Aqui estão as saídas:
Chrome 75 (substring
vitórias):
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
vitórias):
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
vence por resultados falsos, startsWith
Vitórias para resultados verdadeiros, também o Safari é o mais rápido em termos de tempo total para executar todo o teste):
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
vence. Os resultados são semelhantes ao Chrome e não estou surpreso, pois a ópera é baseada em cromo e pisca):
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}
Acontece que todos os navegadores têm seus próprios detalhes de implementação (Opera Apart, que é baseado no Chrome e no Chrome e Blink).
Obviamente, testes adicionais com diferentes casos de uso podem e devem ser realizados (por exemplo, quando a agulha é realmente curta em comparação com o palheiro, quando o palheiro é mais curto que a agulha, etc ...), mas no meu caso eu precisava comparar cordas muito longas e queria compartilhar aqui.
Se você está trabalhando com startsWith()
e endsWith()
Então você deve ter cuidado com os principais espaços. Aqui está um exemplo completo:
var str1 = " Your String Value Here.!! "; // Starts & ends with spaces
if (str1.startsWith("Your")) { } // returns FALSE due to the leading spaces…
if (str1.endsWith("Here.!!")) { } // returns FALSE due to trailing spaces…
var str2 = str1.trim(); // Removes all spaces (and other white-space) from start and end of `str1`.
if (str2.startsWith("Your")) { } // returns TRUE
if (str2.endsWith("Here.!!")) { } // returns TRUE
Você também pode devolver todos os membros de uma matriz que começam com uma string criando seu próprio protótipo / extensão ao protótipo da matriz, também conhecido como
Array.prototype.mySearch = function (target) {
if (typeof String.prototype.startsWith != 'function') {
String.prototype.startsWith = function (str){
return this.slice(0, str.length) == str;
};
}
var retValues = [];
for (var i = 0; i < this.length; i++) {
if (this[i].startsWith(target)) { retValues.push(this[i]); }
}
return retValues;
};
E para usá -lo:
var myArray = ['Hello', 'Helium', 'Hideout', 'Hamster'];
var myResult = myArray.mySearch('Hel');
// result -> Hello, Helium