C # में एक बड़े स्ट्रिंग में सबस्ट्रिंग के सभी पदों को खोजना


83

मेरे पास एक बड़ा स्ट्रिंग है जिसे मुझे पार्स करने की आवश्यकता है, और मुझे सभी उदाहरणों को खोजने extract"(me,i-have lots. of]punctuationऔर प्रत्येक की सूची को एक सूची में संग्रहीत करने की आवश्यकता है।

तो कहते हैं कि स्ट्रिंग का यह टुकड़ा बड़े स्ट्रिंग की शुरुआत और मध्य में था, दोनों को मिल जाएगा, और उनके अनुक्रमणिका को जोड़ दिया जाएगा List। और Listइसमें 0कुछ भी शामिल होगा और अन्य सूचकांक।

मैं चारों ओर खेलने गया है, और string.IndexOfकरता है लगभग मैं क्या देख रहा हूँ, और मैं कुछ कोड लिखा है - लेकिन यह काम नहीं कर रहा है और मैं यह पता लगाने की वास्तव में क्या गलत है असमर्थ रहे हैं:

List<int> inst = new List<int>();
int index = 0;
while (index < source.LastIndexOf("extract\"(me,i-have lots. of]punctuation", 0) + 39)
{
    int src = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index);
    inst.Add(src);
    index = src + 40;
}
  • inst = सूची
  • source = बड़ी कडी

कोई बेहतर विचार?

जवाबों:


142

यहाँ इसके लिए एक उदाहरण विस्तार विधि दी गई है:

public static List<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    List<int> indexes = new List<int>();
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            return indexes;
        indexes.Add(index);
    }
}

यदि आप इसे एक स्थिर वर्ग में रखते हैं और usingइसके साथ नेमस्पेस आयात करते हैं , तो यह किसी भी स्ट्रिंग पर एक विधि के रूप में प्रकट होता है, और आप बस कर सकते हैं:

List<int> indexes = "fooStringfooBar".AllIndexesOf("foo");

विस्तार विधियों की अधिक जानकारी के लिए, http://msdn.microsoft.com/en-us/library/bb383977.aspx

इसके अलावा एक पुनरावृत्ति का उपयोग करते हुए:

public static IEnumerable<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            break;
        yield return index;
    }
}

8
क्यों नहीं अनुक्रमित सूची के बजाय IEnumerable <int> और उपज वापसी सूचकांक का उपयोग करें?
m0sa

2
@ m0sa: अच्छी बात है। इसके मज़े के लिए एक और संस्करण जोड़ा।
मैटी विर्ककुनेन

2
@ PedroC88: उपयोग yieldकरने से कोड "आलसी" हो जाएगा। यह विधि के भीतर सभी इंडेक्स को इन-मेमोरी सूची में एकत्र नहीं करेगा। किस तरह का व्यावहारिक प्रभाव प्रदर्शन पर पड़ता है यह बहुत सारे कारकों पर निर्भर करता है।
मत्ती विर्ककुनेन

1
@Paul: "मई नहीं" जैसा कि "नहीं होना चाहिए"। यदि आपको शब्दांकन पसंद नहीं है तो आप हमेशा एक संपादन का सुझाव दे सकते हैं, लेकिन मुझे नहीं लगता कि यह समझना मुश्किल है।
मत्ती वीरकुंकेन

10
ध्यान! जोड़ने के कारण value.Lengthआपको नेस्टेड मैच याद आ सकते हैं! उदाहरण: "यह एक नेस्टेडनस्टेस्ड मैच टेस्ट है!" "नेस्टेडनस्टेड" के लिए मिलान के साथ केवल एक इंडेक्स मिलेगा, लेकिन नेस्टेड एक नहीं। इसे ठीक करने के लिए +=1इसके बजाय लूप में जोड़ें +=value.Length
क्रिस्टोफ मेयनर

20

आप RegEx वर्ग में निर्मित का उपयोग क्यों नहीं करते हैं:

public static IEnumerable<int> GetAllIndexes(this string source, string matchString)
{
   matchString = Regex.Escape(matchString);
   foreach (Match match in Regex.Matches(source, matchString))
   {
      yield return match.Index;
   }
}

यदि आपको अभिव्यक्ति का पुन: उपयोग करने की आवश्यकता है तो इसे संकलित करें और इसे कहीं पर कैश करें। पुन: उपयोग मामले के लिए एक और अधिभार में मैचस्ट्रीमिंग को Regex matchExpression के लिए मैचस्ट्रीम परम में बदलें।


यह संकलन नहीं करता है
अंशुल

क्या है indexes? यह कहीं भी परिभाषित नहीं है।
सागियो

मेरा बुरा यह एक अवशेष है। उस लाइन को हटा दें।
csaam

2
सावधान रहें कि इस विधि में स्वीकृत उत्तर के समान दोष है। यदि आपका स्रोत स्ट्रिंग "ccc" है और पैटर्न "cc" है तो यह केवल एक ही घटना लौटाएगा।
user280498

15

LINQ का उपयोग कर

public static IEnumerable<int> IndexOfAll(this string sourceString, string subString)
{
    return Regex.Matches(sourceString, subString).Cast<Match>().Select(m => m.Index);
}

2
हालांकि आप सबस्ट्रिंग से बचना भूल गए।
csaam

यह अपने निम्न चक्रवाती जटिलता के कारण स्वीकृत समाधान के लिए बेहतर है।
डेनी जैकब

5

पॉलिश संस्करण + मामले की अनदेखी का समर्थन:

public static int[] AllIndexesOf(string str, string substr, bool ignoreCase = false)
{
    if (string.IsNullOrWhiteSpace(str) ||
        string.IsNullOrWhiteSpace(substr))
    {
        throw new ArgumentException("String or substring is not specified.");
    }

    var indexes = new List<int>();
    int index = 0;

    while ((index = str.IndexOf(substr, index, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) != -1)
    {
        indexes.Add(index++);
    }

    return indexes.ToArray();
}

2

यह O (N + M) में KMP एल्गोरिथ्म का उपयोग करते हुए कुशल समय जटिलता में किया जा सकता है जहां N की लंबाई है textऔर M की लंबाई है pattern

यह कार्यान्वयन और उपयोग है:

static class StringExtensions
{
    public static IEnumerable<int> AllIndicesOf(this string text, string pattern)
    {
        if (string.IsNullOrEmpty(pattern))
        {
            throw new ArgumentNullException(nameof(pattern));
        }
        return Kmp(text, pattern);
    }

    private static IEnumerable<int> Kmp(string text, string pattern)
    {
        int M = pattern.Length;
        int N = text.Length;

        int[] lps = LongestPrefixSuffix(pattern);
        int i = 0, j = 0; 

        while (i < N)
        {
            if (pattern[j] == text[i])
            {
                j++;
                i++;
            }
            if (j == M)
            {
                yield return i - j;
                j = lps[j - 1];
            }

            else if (i < N && pattern[j] != text[i])
            {
                if (j != 0)
                {
                    j = lps[j - 1];
                }
                else
                {
                    i++;
                }
            }
        }
    }

    private static int[] LongestPrefixSuffix(string pattern)
    {
        int[] lps = new int[pattern.Length];
        int length = 0;
        int i = 1;

        while (i < pattern.Length)
        {
            if (pattern[i] == pattern[length])
            {
                length++;
                lps[i] = length;
                i++;
            }
            else
            {
                if (length != 0)
                {
                    length = lps[length - 1];
                }
                else
                {
                    lps[i] = length;
                    i++;
                }
            }
        }
        return lps;
    }

और यह इसका उपयोग करने का एक उदाहरण है:

static void Main(string[] args)
    {
        string text = "this is a test";
        string pattern = "is";
        foreach (var index in text.AllIndicesOf(pattern))
        {
            Console.WriteLine(index); // 2 5
        }
    }

इष्टतम इंडेक्सऑफ़ कार्यान्वयन की तुलना में इसका प्रदर्शन क्या है, जहां खोज प्रारंभ सूचकांक प्रत्येक सेशन पर पिछले मैच के अंत में सेट है?
caesay

IndexIf की AllIndicesOf से तुलना करना गलत है क्योंकि उनका आउटपुट अलग है। प्रत्येक पुनरावृत्ति में IndexOf विधि का उपयोग करते हुए, O (N ^ 2 M) के लिए समय जटिलता बहुत बढ़ जाती है, जबकि इष्टतम जटिलता O (N + M) है। KMP भोले दृष्टिकोण के समान काम नहीं करता है, यह शुरू से खोज से बचने के लिए एक पूर्वनिर्मित सरणी (LPS) का उपयोग करता है। आप केएमपी एल्गोरिदम को पढ़ने की सलाह देते हैं। विकिपीडिया में "पृष्ठभूमि" अनुभाग के अंतिम पैराग्राफ बताते हैं कि यह ओ (एन) में कैसे काम करता है।
एम। शोर्यानी

1
public List<int> GetPositions(string source, string searchString)
{
    List<int> ret = new List<int>();
    int len = searchString.Length;
    int start = -len;
    while (true)
    {
        start = source.IndexOf(searchString, start + len);
        if (start == -1)
        {
            break;
        }
        else
        {
            ret.Add(start);
        }
    }
    return ret;
}

इसे इस तरह से कॉल करें:

List<int> list = GetPositions("bob is a chowder head bob bob sldfjl", "bob");
// list will contain 0, 22, 26

1

@ माटी विर्ककुनेन द्वारा हाय अच्छा जवाब

public static List<int> AllIndexesOf(this string str, string value) {
    if (String.IsNullOrEmpty(value))
        throw new ArgumentException("the string to find may not be empty", "value");
    List<int> indexes = new List<int>();
    for (int index = 0;; index += value.Length) {
        index = str.IndexOf(value, index);
        if (index == -1)
            return indexes;
        indexes.Add(index);
        index--;
    }
}

लेकिन यह AOOAOOA जैसे परीक्षण मामलों को कवर करता है, जहां विकल्पन है

AOOA और AOOA हैं

आउटपुट 0 और 3


1

Regex के बिना, स्ट्रिंग तुलना प्रकार का उपयोग करना:

string search = "123aa456AA789bb9991AACAA";
string pattern = "AA";
Enumerable.Range(0, search.Length)
   .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; })
   .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length),StringComparison.OrdinalIgnoreCase))
   .Select(searchbit => searchbit.Index)

यह {3,8,19,22} देता है। खाली पैटर्न सभी पदों से मेल खाएगा।

कई पैटर्न के लिए:

string search = "123aa456AA789bb9991AACAA";
string[] patterns = new string[] { "aa", "99" };
patterns.SelectMany(pattern => Enumerable.Range(0, search.Length)
   .Select(index => { return new { Index = index, Length = (index + pattern.Length) > search.Length ? search.Length - index : pattern.Length }; })
   .Where(searchbit => searchbit.Length == pattern.Length && pattern.Equals(search.Substring(searchbit.Index, searchbit.Length), StringComparison.OrdinalIgnoreCase))
   .Select(searchbit => searchbit.Index))

यह {3, 8, 19, 22, 15, 16} देता है


1

@csam सिद्धांत में सही है, हालांकि उसका कोड शिकायत नहीं करेगा और उसे वापस लाया जा सकता है

public static IEnumerable<int> IndexOfAll(this string sourceString, string matchString)
{
    matchString = Regex.Escape(matchString);
    return from Match match in Regex.Matches(sourceString, matchString) select match.Index;
}

यदि उसका कोड गलत था, तो आपने उसे ठीक करने के लिए अपना पद संपादित किया होगा
caesay

मैंने उस पर ध्यान नहीं दिया था। मुझे ऐसा करने के लिए अनिच्छुक होना होगा, बस अगर मैं गलत हूं, तो मुझे नहीं लगता कि मैं ऐसा हूं।
arame3333

बड़े स्ट्रिंग के लिए रेगेक्स का उपयोग करना अच्छा नहीं है। दृष्टिकोण बहुत सारी स्मृति लेता है।
W92

1

मैंने देखा कि कम से कम दो प्रस्तावित समाधान खोज हिट को अतिव्यापी नहीं करते हैं। मैंने हरे रंग के चेकमार्क के साथ चिन्हित नहीं किया। यहाँ वह है जो खोज हिट को अतिव्यापी करता है:

    public static List<int> GetPositions(this string source, string searchString)
    {
        List<int> ret = new List<int>();
        int len = searchString.Length;
        int start = -1;
        while (true)
        {
            start = source.IndexOf(searchString, start +1);
            if (start == -1)
            {
                break;
            }
            else
            {
                ret.Add(start);
            }
        }
        return ret;
    }

0
public static Dictionary<string, IEnumerable<int>> GetWordsPositions(this string input, string[] Susbtrings)
{
    Dictionary<string, IEnumerable<int>> WordsPositions = new Dictionary<string, IEnumerable<int>>();
    IEnumerable<int> IndexOfAll = null;
    foreach (string st in Susbtrings)
    {
        IndexOfAll = Regex.Matches(input, st).Cast<Match>().Select(m => m.Index);
        WordsPositions.Add(st, IndexOfAll);

    }
    return WordsPositions;
}

-1

कोड के आधार पर मैंने एक स्ट्रिंग के कई उदाहरणों को एक बड़े स्ट्रिंग में खोजने के लिए उपयोग किया है, आपका कोड इस तरह दिखेगा:

List<int> inst = new List<int>();
int index = 0;
while (index >=0)
{
    index = source.IndexOf("extract\"(me,i-have lots. of]punctuation", index);
    inst.Add(index);
    index++;
}

यहां दो समस्याएं हैं: पहला, आप हमेशा अपनी परिणाम सूची में -1 जोड़ते हैं, जो एक मान्य परिणाम नहीं है। दूसरा, indexOf-1 और लौटने के कारण कोड समाप्त नहीं होता है index++। अगर -1 का परिणाम है, तो मैं इसके while (true)साथ प्रयोग करूंगा । break;IndexOf
b-पॉज़ 465

-1

मुझे यह उदाहरण मिला और इसे एक समारोह में शामिल किया गया:

    public static int solution1(int A, int B)
    {
        // Check if A and B are in [0...999,999,999]
        if ( (A >= 0 && A <= 999999999) && (B >= 0 && B <= 999999999))
        {
            if (A == 0 && B == 0)
            {
                return 0;
            }
            // Make sure A < B
            if (A < B)
            {                    
                // Convert A and B to strings
                string a = A.ToString();
                string b = B.ToString();
                int index = 0;

                // See if A is a substring of B
                if (b.Contains(a))
                {
                    // Find index where A is
                    if (b.IndexOf(a) != -1)
                    {                            
                        while ((index = b.IndexOf(a, index)) != -1)
                        {
                            Console.WriteLine(A + " found at position " + index);
                            index++;
                        }
                        Console.ReadLine();
                        return b.IndexOf(a);
                    }
                    else
                        return -1;
                }
                else
                {
                    Console.WriteLine(A + " is not in " + B + ".");
                    Console.ReadLine();

                    return -1;
                }
            }
            else
            {
                Console.WriteLine(A + " must be less than " + B + ".");
               // Console.ReadLine();

                return -1;
            }                
        }
        else
        {
            Console.WriteLine("A or B is out of range.");
            //Console.ReadLine();

            return -1;
        }
    }

    static void Main(string[] args)
    {
        int A = 53, B = 1953786;
        int C = 78, D = 195378678;
        int E = 57, F = 153786;

        solution1(A, B);
        solution1(C, D);
        solution1(E, F);

        Console.WriteLine();
    }

यह दिखाता है:

53 स्थिति 2 पर पाया गया

पद पर पाया गया ४
7 में स्थान 78 पर पाया गया

57 153786 में नहीं है


1
हाय मार्क, मैं देख रहा हूँ कि आप stackoverflow के लिए नए हैं। यह उत्तर इस पुराने प्रश्न से कुछ भी जोड़ता नहीं है, पहले से ही बहुत बेहतर उत्तर हैं। यदि भविष्य में इस तरह के प्रश्न का उत्तर दिया जाए, तो कृपया यह समझाने का प्रयास करें कि आपके उत्तर में कुछ जानकारी या मूल्य क्यों हैं जो पहले से ही अन्य उत्तरों में मौजूद नहीं हैं।
19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.