एक स्ट्रिंग की nth घटना का सूचकांक प्राप्त करें?


100

जब तक मैं एक स्पष्ट अंतर्निहित पद्धति को याद नहीं कर रहा हूं, तब तक स्ट्रिंग के भीतर एन स्ट्रिंग की एन घटना प्राप्त करने का सबसे तेज़ तरीका क्या है ?

मुझे पता है कि मैं लूप के प्रत्येक पुनरावृत्ति पर इसके स्टार्ट इंडेक्स को अपडेट करके IndexOf विधि को लूप कर सकता हूं । लेकिन इसे इस तरह करना मुझे बेकार लगता है।


मैं उसके लिए एक नियमित अभिव्यक्ति का उपयोग करूंगा फिर आपको स्ट्रिंग के भीतर स्ट्रिंग मिलान करने का इष्टतम तरीका है। यह एक सुंदर DSLs में हम सभी को संभव होने पर उपयोग करना चाहिए। VB.net कोड में एक उदाहरण C # में लगभग समान है।
बोवियम

2
मैं नियमित अभिव्यक्तियों के संस्करण पर अच्छा पैसा लगाऊंगा, जो कि "लूपिंग रखना और सरल स्ट्रिंग करना। IndexOf" की तुलना में सही होने के लिए काफी कठिन है। नियमित अभिव्यक्तियों में अपना स्थान होता है, लेकिन इसका उपयोग तब नहीं किया जाना चाहिए जब सरल विकल्प मौजूद हों।
जॉन स्कीट

जवाबों:


52

मूल रूप से आपको क्या करना है - या कम से कम, यह सबसे आसान समाधान है। यदि आप इसके बारे में सोचते हैं, तो आप "बर्बाद कर रहे हैं" एन विधि चालान की लागत होगी - आप वास्तव में किसी भी मामले की दो बार जांच नहीं करेंगे। (IndexOf मैच मिलते ही वापस आ जाएगा, और आप उसे वहीं छोड़ देंगे जहाँ से वह रवाना हुआ था।)


2
मुझे लगता है कि आपका अधिकार है, ऐसा लगता है कि वहाँ एक विधि में बनाया जाना चाहिए, हालांकि मुझे यकीन है कि यह एक सामयिक घटना है।
पीटीई

4
वास्तव में? मुझे यह याद नहीं है कि जावा और सी # विकास के बारे में 13 वर्षों में क्या करना है। इसका मतलब यह नहीं है कि मैं वास्तव में ऐसा करने के लिए कभी नहीं किया है - लेकिन बस याद करने के लिए अक्सर पर्याप्त नहीं है।
जॉन स्कीट

जावा की बात करें तो हमारे पास है StringUtils.ordinalIndexOf()। सभी लाइनक और अन्य अद्भुत विशेषताओं के साथ सी #, इसके लिए केवल एक अंतर्निहित समर्थन नहीं है। और हां, अगर आप पार्सर्स और टोकनधारकों के साथ काम कर रहे हैं तो इसका समर्थन होना बहुत जरूरी है।
एनी

3
@ एनी: आप कहते हैं कि "हमारे पास" - क्या आपका मतलब अपाचे कॉमन्स में है? यदि हां, तो आप अपने स्वयं के तीसरे पक्ष के पुस्तकालय को .NET के लिए आसानी से लिख सकते हैं जितनी आसानी से आप जावा के लिए कर सकते हैं ... तो ऐसा नहीं है कि कुछ ऐसा है जो जावा मानक पुस्तकालय में है। .NET नहीं है। और निश्चित रूप से सी # में आप इसे string:) पर विस्तार विधि के रूप में जोड़ सकते हैं
जॉन स्कीट

108

आप वास्तव /((s).*?){n}/में प्रतिस्थापन की n- वीं घटना के लिए खोज करने के लिए नियमित अभिव्यक्ति का उपयोग कर सकते हैं s

C # में ऐसा लग सकता है:

public static class StringExtender
{
    public static int NthIndexOf(this string target, string value, int n)
    {
        Match m = Regex.Match(target, "((" + Regex.Escape(value) + ").*?){" + n + "}");

        if (m.Success)
            return m.Groups[2].Captures[n - 1].Index;
        else
            return -1;
    }
}

नोट: मैंने Regex.Escapeमूल समाधानों को जोड़ दिया है ताकि उन खोज चरित्रों का पता लगाया जा सके जिनका rexx इंजन के लिए विशेष अर्थ है।


2
क्या आपको बचना चाहिए value? मेरे मामले में मैं एक बिंदु की तलाश कर रहा था msdn.microsoft.com/en-us/library/…
russau

3
यदि लक्ष्य स्ट्रिंग में लाइनब्रीक्स हैं, तो यह Regex काम नहीं करता है। क्या आप इसे ठीक कर सकते हैं? धन्यवाद।
इग्नासियो सोलर गार्सिया

अगर कोई Nth मैच नहीं है तो लॉक लगता है। मुझे 1000 मानों के लिए अल्पविराम से अलग मान को सीमित करने की आवश्यकता थी, और यह तब कम हो गया जब सीएसवी कम था। इसलिए @ योगेश - जैसा कि एक महान स्वीकृत उत्तर नहीं है। ;) इस उत्तर के एक संस्करण का उपयोग करना (यहां स्ट्रिंग से स्ट्रिंग संस्करण है ) और इसके बजाय एनटीटी गिनती पर रुकने के लिए लूप को बदल दिया
Ruffin

\ पर खोज करने की कोशिश कर रहा है, मान "\\" में पारित हो गया है, और मैच स्ट्रिंग इस तरह दिखता है इससे पहले कि regex.match फ़ंक्शन: () (? *?) {2}। मुझे यह त्रुटि मिलती है: पार्सिंग "()। *?) {2}" - पर्याप्त नहीं)। त्रुटि के बिना बैक स्लैश देखने का सही प्रारूप क्या है?
रिचीएमएन

3
क्षमा करें, लेकिन एक छोटी सी आलोचना: रेगेक्स समाधान उप-रूपी हैं, क्योंकि तब मुझे nth समय के लिए regexs को पुनः जारी करना होगा। जब regexes का उपयोग किया जाता है तो कोड को पढ़ना अधिक मुश्किल होता है।
मार्क रोजर्स

19

मूल रूप से आपको क्या करना है - या कम से कम, यह सबसे आसान समाधान है। यदि आप इसके बारे में सोचते हैं, तो आप "बर्बाद कर रहे हैं" एन विधि चालान की लागत होगी - आप वास्तव में किसी भी मामले की दो बार जांच नहीं करेंगे। (IndexOf मैच मिलते ही वापस आ जाएगा, और आप उसे वहीं छोड़ देंगे जहाँ से वह रवाना हुआ था।)

यहाँ (ऊपर की पुनरावर्ती कार्यान्वयन है विचार ) एक विस्तार पद्धति के रूप में, ढांचे विधि (ओं) का प्रारूप mimicing:

public static int IndexOfNth(this string input,
                             string value, int startIndex, int nth)
{
    if (nth < 1)
        throw new NotSupportedException("Param 'nth' must be greater than 0!");
    if (nth == 1)
        return input.IndexOf(value, startIndex);
    var idx = input.IndexOf(value, startIndex);
    if (idx == -1)
        return -1;
    return input.IndexOfNth(value, idx + 1, --nth);
}

इसके अलावा, यहां कुछ (MBUnit) यूनिट परीक्षण हैं जो आपकी मदद कर सकते हैं (यह साबित करने के लिए कि यह सही है):

using System;
using MbUnit.Framework;

namespace IndexOfNthTest
{
    [TestFixture]
    public class Tests
    {
        //has 4 instances of the 
        private const string Input = "TestTest";
        private const string Token = "Test";

        /* Test for 0th index */

        [Test]
        public void TestZero()
        {
            Assert.Throws<NotSupportedException>(
                () => Input.IndexOfNth(Token, 0, 0));
        }

        /* Test the two standard cases (1st and 2nd) */

        [Test]
        public void TestFirst()
        {
            Assert.AreEqual(0, Input.IndexOfNth("Test", 0, 1));
        }

        [Test]
        public void TestSecond()
        {
            Assert.AreEqual(4, Input.IndexOfNth("Test", 0, 2));
        }

        /* Test the 'out of bounds' case */

        [Test]
        public void TestThird()
        {
            Assert.AreEqual(-1, Input.IndexOfNth("Test", 0, 3));
        }

        /* Test the offset case (in and out of bounds) */

        [Test]
        public void TestFirstWithOneOffset()
        {
            Assert.AreEqual(4, Input.IndexOfNth("Test", 4, 1));
        }

        [Test]
        public void TestFirstWithTwoOffsets()
        {
            Assert.AreEqual(-1, Input.IndexOfNth("Test", 8, 1));
        }
    }
}

मैंने वेस्टन की महान प्रतिक्रिया (धन्यवाद वेस्टन) के आधार पर अपने प्रारूपण और परीक्षण मामलों को अद्यतन किया है।
टॉड थॉमसन

14
private int IndexOfOccurence(string s, string match, int occurence)
{
    int i = 1;
    int index = 0;

    while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1)
    {
        if (i == occurence)
            return index;

        i++;
    }

    return -1;
}

या विस्तार विधियों के साथ सी # में

public static int IndexOfOccurence(this string s, string match, int occurence)
{
    int i = 1;
    int index = 0;

    while (i <= occurence && (index = s.IndexOf(match, index + 1)) != -1)
    {
        if (i == occurence)
            return index;

        i++;
    }

    return -1;
}

5
यदि मैं गलत नहीं हूं, तो यह विधि विफल हो जाती है यदि स्ट्रिंग मैच के लिए स्थिति 0 पर शुरू होती है, जिसे indexशुरुआत में -1 पर सेट करके ठीक किया जा सकता है ।
पीटर मजीद

1
तुम भी नल या खाली तार के लिए जाँच करना चाहते हो सकता है और मैच या यह फेंक देंगे, लेकिन एक डिजाइन निर्णय thats।

धन्यवाद @PeterMajeed - यदि "BOB".IndexOf("B")रिटर्न 0 है, तो इस समारोह के लिएIndexOfOccurence("BOB", "B", 1)
पीटरएक्स

2
तुम्हारा संभवत: अंतिम समाधान है क्योंकि इसमें दोनों का विस्तार कार्य है और यह regexs और पुनरावृत्ति से बचा जाता है, दोनों कोड को कम पठनीय बनाते हैं।
मार्क रोजर्स

दरअसल @tdyen, कोड विश्लेषण जारी करेगा : "CA1062 सार्वजनिक विधियों में से मान्य तर्क" अगर IndexOfOccurenceजाँच नहीं करता है, तो sहै null। और String.IndexOf (स्ट्रिंग, Int32)ArgumentNullException अगर matchहै तो फेंक देंगे null
डेविड आरआर

1

शायद String.Split()विधि के साथ काम करना और जाँचना अच्छा होगा कि अनुरोधित घटना सरणी में है, यदि आपको सूचकांक की आवश्यकता नहीं है, लेकिन सूचकांक पर मूल्य


1

कुछ बेंचमार्किंग के बाद, यह सबसे सरल और सबसे प्रभावी समाधान लगता है

public static int IndexOfNthSB(string input,
             char value, int startIndex, int nth)
        {
            if (nth < 1)
                throw new NotSupportedException("Param 'nth' must be greater than 0!");
            var nResult = 0;
            for (int i = startIndex; i < input.Length; i++)
            {
                if (input[i] == value)
                    nResult++;
                if (nResult == nth)
                    return i;
            }
            return -1;
        }

1

सिस्टम। वाल्टअप फुट:

var index = line.Select((x, i) => (x, i)).Where(x => x.Item1 == '"').ElementAt(5).Item2;

उस से एक समारोह लेखन होमवर्क है


0

टॉड का उत्तर कुछ हद तक सरल किया जा सकता है।

using System;

static class MainClass {
    private static int IndexOfNth(this string target, string substring,
                                       int seqNr, int startIdx = 0)
    {
        if (seqNr < 1)
        {
            throw new IndexOutOfRangeException("Parameter 'nth' must be greater than 0.");
        }

        var idx = target.IndexOf(substring, startIdx);

        if (idx < 0 || seqNr == 1) { return idx; }

        return target.IndexOfNth(substring, --seqNr, ++idx); // skip
    }

    static void Main () {
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 1));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 2));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 3));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 4));
    }
}

उत्पादन

1
3
5
-1

0

या लूप करते समय कुछ इस तरह से

 private static int OrdinalIndexOf(string str, string substr, int n)
    {
        int pos = -1;
        do
        {
            pos = str.IndexOf(substr, pos + 1);
        } while (n-- > 0 && pos != -1);
        return pos;
    }

-4

यह यह कर सकता है:

Console.WriteLine(str.IndexOf((@"\")+2)+1);

2
मैं नहीं देखता कि यह कैसे काम करेगा। क्या आप इसका संक्षिप्त विवरण शामिल कर सकते हैं?
बॉब कॉफ़मैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.