كيفية التحقق مما إذا كانت سلسلة "startswith" سلسلة أخرى؟

StackOverflow https://stackoverflow.com/questions/646628

  •  22-07-2019
  •  | 
  •  

سؤال

كيف أكتب ما يعادل C# String.StartsWith في JavaScript؟

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

haystack.startsWith(needle) == true

ملاحظة: هذا سؤال قديم ، وكما أشرت في التعليقات ، قدم ECMASCRIPT 2015 (ES6) .startsWith طريقة. ومع ذلك ، في وقت كتابة هذا التحديث (2015) دعم المتصفح أبعد ما يكون عن اكتماله.

هل كانت مفيدة؟

المحلول

يمكنك استخدام ecmascript 6's String.prototype.startsWith() الطريقة ، لكنها لم تدعم بعد في جميع المتصفحات. ستحتاج إلى استخدام Shim/Polyfill لإضافته على المتصفحات التي لا تدعمه. إنشاء تنفيذ يتوافق مع جميع التفاصيل الموضوعة في المواصفات هو معقد بعض الشيء. إذا كنت تريد شيم المؤمن ، استخدم إما:

بمجرد أن تتلألأ الطريقة (أو إذا كنت تدعم فقط المتصفحات ومحركات 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.
  • لا ينشئ سلسلة مؤقتة جديدة ثم تجاهلها على الفور.
data.substring(0, input.length) === input

بدون وظيفة المساعد ، فقط باستخدام Regex's .test طريقة:

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

للقيام بذلك بسلسلة ديناميكية بدلاً من سلسلة متشددين (على افتراض أن السلسلة لن تحتوي على أي أحرف تحكم regexp):

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

يجب عليك التحقق هل هناك وظيفة regexp.escape في JavaScript؟ في حالة وجود احتمال ظهور أحرف التحكم في REGEXP في السلسلة.

أفضل حل:

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

وهنا endswith إذا كنت بحاجة إلى ذلك أيضًا:

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

لأولئك الذين يفضلون النموذج الأولي في سلسلة:

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 (Harmony) يقدم مواطنا startsWith طريقة للسلاسل.
فقط فكر في مقدار الوقت الذي كان سيتم حفظه إذا فكروا في تضمين هذه الطريقة التي تشتد الحاجة إليها في الإصدار الأولي نفسه.

تحديث

كما أشار ستيف (التعليق الأول على هذه الإجابة) ، ستقوم الوظيفة المخصصة أعلاه برمي خطأ إذا كانت المعطاة بادئة أقصر من السلسلة بأكملها. لقد أصلح ذلك وأضاف تحسين الحلقة يمكن الاطلاع عليه http://jsperf.com/startswith2/4.

لاحظ أن هناك تحسين حلقة تضمنه ستيف ، أظهر الأول من الاثنين أداءً أفضل ، وبالتالي سأقوم بنشر هذا الرمز أدناه:

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 والتحضير لذلك يجب على المرء استخدام polyfill "الرسمي" من أجل منع المشاكل والدموع المستقبلية.

لحسن الحظ ، يوفر لنا الخبراء في 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. (تأكد من التحقق من الإصدارات اللاحقة التي قد تكون قد غمرت لي.)

لقد تعلمت للتو عن مكتبة السلسلة هذه:

http://stringjs.com/

قم بتضمين ملف 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 (ويكتمل وظيفيًا بقدر ما أستطيع أن أقول).

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

استند هذا إلى StartSwith2 من هنا: http://jsperf.com/startswith2/6. لقد أضفت قرصًا صغيرًا لتحسين الأداء الصغير ، ومنذ ذلك الحين أضفت أيضًا فحصًا لسلسلة المقارنة التي لا ، غير محددة ، وقامت بتحويلها لإضافتها إلى النموذج الأولي للسلسلة باستخدام التقنية في إجابة CMS.

لاحظ أن هذا التنفيذ لا يدعم المعلمة "الموضع" المذكورة في هذا شبكة مطور موزيلا الصفحة ، ولكن هذا لا يبدو أنه جزء من اقتراح ECMASCRIPT على أي حال.

لست متأكدًا من JavaScript ولكن في TypeScript فعلت شيئًا مثل

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

أعتقد أنه يجب أن يعمل على JS أيضًا. اتمني ان يكون مفيدا!

  1. السؤال قديم بعض الشيء ، لكنني أردت أن أكتب هذه الإجابة لتظهر لك بعض المعايير التي أدليت بها بناءً على جميع الإجابات المقدمة هنا و JSPerf التي يشاركها Jim Buck.

احتجت أساسًا إلى طريقة سريعة للعثور على ما إذا كانت الإبرة الطويلة ضمن كومة قش طويلة وهي متشابهة جدًا باستثناء الأحرف الأخيرة.

إليك الرمز الذي كتبته والذي يختبر لكل وظيفة (لصق ، فرعية ، startswith ، إلخ) كلاهما عندما يعودون خطأ وصحيح ضد سلسلة كومة القش (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, سفاري 12 و أوبرا 62.

لم أقم بتضمين Edge و IE لأنني لا أملكها على هذا الجهاز ، ولكن إذا أراد شخص ما تشغيل البرنامج النصي مقابل Edge وعلى الأقل 9 ومشاركة الإخراج هنا فسأكون فضوليًا للغاية لرؤية النتائج.

فقط تذكر أنك تحتاج إلى إعادة إنشاء الأوتار الثلاثة الطويلة وحفظ البرنامج النصي في ملف يتم فتحه بعد ذلك في متصفحك كنسخ/لصق على وحدة التحكم في المستعرض سيمنعه لأن طول كل سلسلة هو> = 1.000.000).

هنا المخرجات:

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

أوبرا 62 (substring يفوز. النتائج مشابهة للكروم وأنا لست مندهشًا لأن الأوبرا تعتمد على الكروم والميض):

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}

اتضح أن كل متصفح لديه تفاصيل تنفيذ خاصة به (البضع الأوبرا ، الذي يعتمد على الكروم والكروم في Chrome).

بالطبع ، يمكن وينبغي إجراء مزيد من الاختبار مع حالات الاستخدام المختلفة (على سبيل المثال عندما تكون الإبرة قصيرة حقًا مقارنةً بمقاربة Haystack ، عندما تكون Haystack أقصر من الإبرة ، إلخ ...) ، لكن في حالتي ، كنت بحاجة لمقارنة سلاسل طويلة جدًا و أراد مشاركته هنا.

إذا كنت تعمل مع 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top