Как проверить, является ли строка "StartsWith" другой строкой?
Как бы я написал эквивалент C# String.StartsWith
в JavaScript?
var haystack = 'hello world';
var needle = 'he';
haystack.startsWith(needle) == true
Примечание. Это старый вопрос, и, как указано в комментариях, ECMAScript 2015 (ES6) представил .startsWith
метод. Однако на момент написания этого обновления (2015) поддержка браузера еще далека от завершения.
21 ответ
Вы можете использовать ECMAScript 6 String.prototype.startsWith()
метод, но он еще не поддерживается во всех браузерах. Вы захотите использовать shim/polyfill для добавления его в браузеры, которые его не поддерживают. Создание реализации, которая соответствует всем деталям, изложенным в спецификации, немного сложнее, и версия, определенная в этом ответе, не подойдет; если вам нужна верная прокладка, используйте либо:
- Матиас Биненс
String.prototype.startsWith
шим или - Шим es6, который максимально упрощает спецификацию ES6, включая
String.prototype.startsWith
,
После того, как вы изменили метод (или если вы поддерживаете только браузеры и механизмы JavaScript, у которых он уже есть), вы можете использовать его следующим образом:
"Hello World!".startsWith("He"); // true
var haystack = "Hello world";
var prefix = 'orl';
haystack.startsWith(prefix); // false
Еще одна альтернатива с .lastIndexOf
:
haystack.lastIndexOf(needle, 0) === 0
Это смотрит назад через haystack
для возникновения needle
начиная с индекса 0
из haystack
, Другими словами, он только проверяет, haystack
начинается с needle
,
В принципе, это должно иметь преимущества в производительности по сравнению с некоторыми другими подходами:
- Он не ищет весь
haystack
, - Он не создает новую временную строку, а затем сразу же удаляет ее.
Без вспомогательной функции, просто с помощью регулярных выражений .test
метод:
/^He/.test('Hello world')
Чтобы сделать это с динамической строкой, а не жестко закодированной (при условии, что строка не будет содержать никаких управляющих символов регулярного выражения):
new RegExp('^' + needle).test(haystack)
Вы должны проверить Есть ли функция RegExp.escape в Javascript? если существует возможность, что управляющие символы регулярного выражения появляются в строке.
Лучшее решение:
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
И здесь заканчивается, если вам это тоже нужно:
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
Я просто хотел добавить свое мнение по этому поводу.
Я думаю, что мы можем просто использовать так:
var haystack = 'hello world';
var needle = 'he';
if (haystack.indexOf(needle) == 0) {
// Code if string starts with this substring
}
Вот небольшое улучшение решения 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
хотя не так читабельно.
Также проверьте underscore.string.js. Он поставляется с кучей полезных методов тестирования строк и манипуляций, в том числе startsWith
метод. Из документов:
начинается с
_.startsWith(string, starts)
Этот метод проверяет,
string
начинается сstarts
,_("image.gif").startsWith("image") => true
Я недавно задал себе тот же вопрос.
Есть несколько возможных решений, вот 3 из них:
s.indexOf(starter) === 0
s.substr(0,starter.length) === starter
s.lastIndexOf(starter, 0) === 0
(добавлено после просмотра ответа Марка Байерса)используя цикл:
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 (гармония) представляет родной startsWith
метод для строк.
Подумайте, сколько времени было бы сэкономлено, если бы они подумали о включении этого столь необходимого метода в самой первоначальной версии.
Обновить
Как указал Стив (первый комментарий к этому ответу), вышеуказанная пользовательская функция выдаст ошибку, если данный префикс короче всей строки. Он исправил это и добавил оптимизацию цикла, которую можно посмотреть по адресу http://jsperf.com/startswith2/4.
Обратите внимание, что есть 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;
}
Поскольку это настолько популярно, я думаю, что стоит отметить, что в ECMA 6 есть реализация этого метода, и при подготовке к этому следует использовать "официальный" полифилл, чтобы предотвратить будущие проблемы и разрывы.
К счастью, специалисты Mozilla предоставляют нам один:
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.
Лучшее решение - перестать использовать библиотечные вызовы и просто признать, что вы работаете с двумя массивами. Ручная реализация является как короткой, так и более быстрой, чем любое другое решение, которое я видел здесь.
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. (Убедитесь, что вы проверили более поздние версии, которые могли превзойти мою.)
- Вопрос немного устарел, но я хотел написать этот ответ, чтобы показать вам некоторые тесты, которые я сделал на основе всех предоставленных здесь ответов и jsperf, предоставленного Джимом Баком.
Мне в основном нужен был быстрый способ определить, находится ли длинная игла в длинном стоге сена, и они очень похожи, за исключением последних символов.
Вот код, который я написал, который для каждой функции (splice, substring, startWith и т. Д.) Проверяет оба, когда они возвращают false и true для строки стога сена (nestedString
) из 1.000.0001 символа и ложной или ложной игольной строки из 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 75, Firefox 67, Safari 12 и Opera 62.
Я не включил Edge и IE, потому что у меня их нет на этой машине, но если кто-то из вас захочет запустить скрипт для Edge и хотя бы IE 9 и поделиться выводом здесь, мне было бы очень любопытно увидеть результаты.
Просто помните, что вам нужно воссоздать 3 длинные строки и сохранить скрипт в файле, который вы затем откроете в своем браузере, поскольку копирование / вставка в консоли браузера заблокирует его, поскольку длина каждой строки>= 1.000.000).
Вот результаты:
Хром 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}
Оказывается, у каждого браузера есть свои особенности реализации (кроме Opera, основанной на Chrome Chromium и Blink).
Конечно, можно и нужно провести дальнейшие испытания с различными вариантами использования (например, когда игла действительно короткая по сравнению со стогом сена, когда стог сена короче иглы и т. Д.), Но в моем случае мне нужно было сравнить очень длинные строки и хотел поделиться этим здесь.
Я только что узнал об этой библиотеке строк:
Включите файл JS, а затем используйте S
переменная, как это:
S('hi there').endsWith('hi there')
Его также можно использовать в NodeJS, установив его:
npm install string
Тогда требуя это как S
переменная:
var S = require('string');
На веб-странице также есть ссылки на библиотеки альтернативных строк, если вам не по вкусу.
var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
return true;
else
return false;
Я искал производительность, поэтому я запускал функции через jsperf. Я проверил функции на предметные и поисковые строки разных размеров, и оказалось, что все методы демонстрируют разную производительность разных входных данных; общая закономерность заключается в том, что производительность уменьшается с увеличением длины строки поиска.
Общий победитель оказывается substr(ing)
метод.
Для иглы с двумя буквами это по-прежнему лучший вариант.
var haystack = 'hello world';
if(haystak[0] === 'h' && haystak[1] === 'e'){
// Needle found
}
В противном случае да, следуйте другим ответам
Строковый объект имеет такие методы, как startWith,ndsWith и включает методы.
StartsWith проверяет, начинается ли данная строка с начала или нет.
endWith проверяет, находится ли данная строка в конце или нет.
включает проверки, присутствует ли данная строка в какой-либо части или нет.
Вы можете найти полную разницу между этими тремя в видео ниже на YouTube.
Основываясь на ответах здесь, это версия, которую я сейчас использую, поскольку она, кажется, дает лучшую производительность, основанную на тестировании 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;
}
}
Это было основано на StartWith2 отсюда: http://jsperf.com/startswith2/6. Я добавил небольшой твик для незначительного улучшения производительности, а также добавил проверку на то, что строка сравнения имеет значение null или undefined, и преобразовал ее для добавления в прототип String, используя технику ответа CMS.
Обратите внимание, что эта реализация не поддерживает параметр "position", который упоминается на этой странице в Mozilla Developer Network, но в любом случае он, похоже, не является частью предложения ECMAScript.
Я не уверен в использовании javascript, но в машинописном тексте я сделал что-то вроде
var str = "something";
(<String>str).startsWith("some");
Думаю, он должен работать и на js. Я надеюсь, что это помогает!
Если вы работаете с startsWith()
а также endsWith()
тогда вы должны быть осторожны с ведущими пробелами. Вот полный пример:
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
Вы также можете вернуть все члены массива, которые начинаются со строки, создав собственный прототип / расширение для прототипа массива, иначе
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;
};
И использовать это:
var myArray = ['Hello', 'Helium', 'Hideout', 'Hamster'];
var myResult = myArray.mySearch('Hel');
// result -> Hello, Helium