Mam duży ciąg, który muszę przeanalizować, i muszę znaleźć wszystkie wystąpienia extract"(me,i-have lots. of]punctuation
i zapisać indeks każdego z nich na liście.
Powiedzmy więc, że ten fragment łańcucha znajdował się na początku i w środku większego ciągu, oba zostaną znalezione, a ich indeksy zostaną dodane do List
. a List
będzie zawierać 0
i inny indeks, cokolwiek by to było.
Bawiłem się, a string.IndexOf
robi prawie to, czego szukam, i napisałem trochę kodu - ale to nie działa i nie byłem w stanie dowiedzieć się, co jest nie tak:
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
= Listasource
= Duży ciąg
Jakieś lepsze pomysły?
yield
spowoduje, że kod będzie „leniwy”. Nie zbierze wszystkich indeksów na liście w pamięci w ramach metody. Jaki praktyczny wpływ ma to na wydajność, zależy od wielu czynników.value.Length
możesz przegapić zagnieżdżone dopasowania! Przykład: „To jest test dopasowania zagnieżdżonego!” z dopasowaniem do „NestedNested” zostanie znaleziony tylko jeden indeks, ale nie zagnieżdżony. Aby to naprawić, po prostu dodaj+=1
pętlę zamiast+=value.Length
.Dlaczego nie używasz wbudowanej klasy 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; } }
Jeśli musisz ponownie użyć wyrażenia, skompiluj je i gdzieś w pamięci podręcznej. Zmień parametr matchString na Regex matchExpression w innym przeciążeniu dla przypadku ponownego użycia.
źródło
indexes
? Nigdzie nie jest zdefiniowane.przy użyciu LINQ
public static IEnumerable<int> IndexOfAll(this string sourceString, string subString) { return Regex.Matches(sourceString, subString).Cast<Match>().Select(m => m.Index); }
źródło
Wersja polerowana + obsługa ignorowania wielkości liter:
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(); }
źródło
Można to zrobić w efektywnej złożoności czasowej za pomocą algorytmu KMP w O (N + M), gdzie N to długość,
text
a M to długośćpattern
.Oto implementacja i użycie:
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; }
a to jest przykład jak z niego korzystać:
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 } }
źródło
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; }
Nazwij to tak:
List<int> list = GetPositions("bob is a chowder head bob bob sldfjl", "bob"); // list will contain 0, 22, 26
źródło
Cześć, dobra odpowiedź od @Matti Virkkunen
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--; } }
Ale to obejmuje przypadki testowe, takie jak AOOAOOA, gdzie podciąg
są AOOA i AOOA
Wyjście 0 i 3
źródło
Bez Regex, przy użyciu typu porównania ciągów:
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)
Zwraca to {3,8,19,22}. Pusty wzorzec pasowałby do wszystkich pozycji.
W przypadku wielu wzorów:
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))
Zwraca to {3, 8, 19, 22, 15, 16}
źródło
@csam jest poprawny w teorii, chociaż jego kod nie będzie zgodny i można go załamać
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; }
źródło
Zauważyłem, że co najmniej dwa proponowane rozwiązania nie obsługują nakładających się wyników wyszukiwania. Nie sprawdziłem tego oznaczonego zielonym haczykiem. Oto taki, który obsługuje nakładające się wyniki wyszukiwania:
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; }
źródło
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; }
źródło
W oparciu o kod, którego użyłem do znalezienia wielu wystąpień ciągu w większym ciągu, Twój kod wyglądałby następująco:
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++; }
źródło
indexOf
zwrócenia -1 iindex++
. Użyłbym awhile (true)
z a,break;
jeśli wynikIndexOf
jest równy -1.Znalazłem ten przykład i umieściłem go w funkcji:
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(); }
Zwroty:
53 znaleziono na pozycji 2
78 w pozycji 4
78 znalezione w pozycji 7
57 nie ma w 153786
źródło