如何检查字符串“StartsWith”是否是另一个字符串?

如何在JavaScript中编写相当于C#的#0

var haystack = 'hello world';var needle = 'he';
haystack.startsWith(needle) == true

注意:这是一个老问题,正如评论中指出的,ECMAScript 2015(ES6)引入了#0方法。然而,在编写此更新(2015)浏览器支持还远未完成时。

1012951 次浏览
data.substring(0, input.length) === input

您可以使用ECMAScript 6的#0方法。它是支持所有主流浏览器。但是,如果您想在不受支持的浏览器中使用它,您需要使用shim/polyill将其添加到这些浏览器上。创建符合所有细节都写在说明书里了的实现有点复杂。如果您想要一个忠实的shim,请使用以下任一:

一旦你对方法进行了微调(或者如果你只支持已经拥有它的浏览器和JavaScript引擎),你可以这样使用它:

console.log("Hello World!".startsWith("He")); // true
var haystack = "Hello world";var prefix = 'orl';console.log(haystack.startsWith(prefix)); // false

以下是对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稍微快一点,也更简洁,尽管可读性不如=== 0

另一种选择#0

haystack.lastIndexOf(needle) === 0

这将通过haystack向后查找needle的出现,从索引字符串长度haystack回到零。换句话说,它只检查haystack是否以needle开头。lastIndexOf提供了第二个可选参数'FromIndex'。如果给定,向后搜索从这个给定的索引位置开始并遍历回索引零。但是我们不能指定最后一个索引之外的任何其他FromIndex,否则搜索可能会忽略一些东西。

原则上,这应该比其他一些方法具有性能优势:

  • 它不会搜索整个haystack
  • 它不会创建一个新的临时字符串,然后立即丢弃它。

没有辅助函数,只需使用regex的#0方法:

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

要使用动态字符串而不是硬编码字符串来执行此操作(假设字符串不包含任何regexp控制字符):

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

如果字符串中可能出现正则表达式控制字符,则应签出JavaScript中是否有RegExp.escape函数?

另请查看underscore.string.js。它带有一系列有用的字符串测试和操作方法,包括startsWith方法。来自文档:

开始_.startsWith(string, starts)

此方法检查string是否以starts开头。

_("image.gif").startsWith("image")=> true
var str = 'hol';var data = 'hola mundo';if (data.length >= str.length && data.substring(0, str.length) == str)return true;elsereturn false;

如果你正在使用startsWith()endsWith(),那么你必须小心前导空格。这是一个完整的例子:

var str1 = " Your String Value Here.!! "; // Starts & ends with spacesif (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 TRUEif (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

我刚刚了解了这个字符串库:

包含js文件,然后像这样使用S变量:

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

它也可以通过安装在NodeJS中使用:

npm install string

然后要求它作为S变量:

var S = require('string');

该网页还具有指向备用字符串库的链接,如果这个不喜欢的话。

我最近也问过自己同样的问题。
有多种可能的解决方案,这里有3个有效的解决方案:

  • s.indexOf(starter) === 0
  • s.substr(0,starter.length) === starter
  • s.lastIndexOf(starter, 0) === 0(在看到Mark Byers的回答后添加)
  • 使用循环:

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

I haven't come across the last solution which makes uses of a loop.
Surprisingly this solution outperforms the first 3 by a significant margin.
Here is the jsperf test I performed to reach this conclusion: http://jsperf.com/startswith2/2

Peace

ps: ecmascript 6 (harmony) introduces a native startsWith method for strings.
Just think how much time would have been saved if they had thought of including this much needed method in the initial version itself.

Update

As Steve pointed out (the first comment on this answer), the above custom function will throw an error if the given prefix is shorter than the whole string. He has fixed that and added a loop optimization which can be viewed at http://jsperf.com/startswith2/4.

Note that there are 2 loop optimizations which Steve included, the first of the two showed better performance, thus I will post that code below:

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

最好的性能解决方案是停止使用库调用,只认识到您正在使用两个数组。

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测试的最佳性能(并且据我所知功能完整)。

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

我添加了一个小调整,以提高性能,并且还添加了一个检查比较字符串是否为空或未定义,并使用CMS答案中的技术将其转换为添加到String原型。

请注意,此实现不支持此Mozilla开发者网络页面中提到的“位置”参数,但这似乎不是ECMAScript提案的一部分。

由于这是如此受欢迎,我认为值得指出的是,在ECMA 6中有一个这种方法的实现,并且在准备该方法时,应该使用“官方”的Poly填充,以防止未来的问题和眼泪。

幸运的是,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时被优雅地忽略的优点。

我只是想补充一下我对此的看法。

我想我们可以这样使用:

var haystack = 'hello world';var needle = 'he';
if (haystack.indexOf(needle) == 0) {// Code if string starts with this substring}

最佳解决方案:

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

这里是结束,如果你也需要:

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

使用方法:

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

我不确定javascript,但在打字稿中我做了类似的事情

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

我想它也应该在js上工作。希望有帮助!

  1. 这个问题有点老,但我想写这个答案来向你展示我根据这里提供的所有答案和Jim Buck分享的jspef做出的一些基准。

我基本上需要一种快速的方法来找到长针是否在长草堆中,除了最后一个字符之外,它们非常相似。

这是我写的代码,对于每个函数(plice、substring、startsWith等),当它们针对1.000.0001个字符的haystack字符串(nestedString)和1.000.000个字符的false sy或truthy针形字符串(分别为testParentStringFalsetestParentStringTrue)返回false和true时,都会进行测试:

// 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 dateStartlet dateEndlet avglet count = 100000const falseResults = []const trueResults = []
/* slice */console.log('========> slice')dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.slice(0, testParentStringFalse.length) === testParentStringFalse}dateEnd = +new Date()avg = (dateEnd - dateStart)/countfalseResults[falseResults.length] = {label: 'slice',avg}console.log(`testString() slice = false`, res, 'avg: ' + avg + 'ms')
dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.slice(0, testParentStringTrue.length) === testParentStringTrue}dateEnd = +new Date()avg = (dateEnd - dateStart)/counttrueResults[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 resfor (let j = 0; j < count; j++) {res = nestedString.lastIndexOf(testParentStringFalse, 0) === 0}dateEnd = +new Date()avg = (dateEnd - dateStart)/countfalseResults[falseResults.length] = {label: 'lastIndexOf',avg}console.log(`testString() lastIndexOf = false`, res, 'avg: ' + avg + 'ms')
dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.lastIndexOf(testParentStringTrue, 0) === 0}dateEnd = +new Date()avg = (dateEnd - dateStart)/counttrueResults[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 resfor (let j = 0; j < count; j++) {res = nestedString.indexOf(testParentStringFalse) === 0}dateEnd = +new Date()avg = (dateEnd - dateStart)/countfalseResults[falseResults.length] = {label: 'indexOf',avg}console.log(`testString() indexOf = false`, res, 'avg: ' + avg + 'ms')
dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.indexOf(testParentStringTrue) === 0}dateEnd = +new Date()avg = (dateEnd - dateStart)/counttrueResults[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 resfor (let j = 0; j < count; j++) {res = nestedString.substring(0, testParentStringFalse.length) === testParentStringFalse}dateEnd = +new Date()avg = (dateEnd - dateStart)/countfalseResults[falseResults.length] = {label: 'substring',avg}console.log(`testString() substring = false`, res, 'avg: ' + avg + 'ms')
dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.substring(0, testParentStringTrue.length) === testParentStringTrue}dateEnd = +new Date()avg = (dateEnd - dateStart)/counttrueResults[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 resfor (let j = 0; j < count; j++) {res = nestedString.startsWith(testParentStringFalse)}dateEnd = +new Date()avg = (dateEnd - dateStart)/countfalseResults[falseResults.length] = {label: 'startsWith',avg}console.log(`testString() startsWith = false`, res, 'avg: ' + avg + 'ms')
dateStart = +new Date()var resfor (let j = 0; j < count; j++) {res = nestedString.startsWith(testParentStringTrue)}dateEnd = +new Date()avg = (dateEnd - dateStart)/counttrueResults[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)}

我在Chrome75Firefox 67Safari12Opera 62上运行了这个基准测试。

我没有包括Edge和IE,因为我在这台机器上没有它们,但是如果你们中的某个人想对Edge和至少IE 9运行脚本并在这里分享输出,我会非常好奇地看到结果。

请记住,您需要重新创建3个长字符串并将脚本保存在一个文件中,然后在浏览器中打开该文件,因为在浏览器的控制台上复制/粘贴会阻止它,因为每个字符串的长度>=1.000.000)。

以下是输出:

Chrome75(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 avg1)  {"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}

Safari12(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)。

当然,可以并且应该使用不同的用例进行进一步的测试(例如,当针头与干草堆相比非常短时,当干草堆比针头短时……),但在我的情况下,我需要比较非常长的字符串,并想在这里分享它。