Tengo una cadena grande que necesito analizar, y necesito encontrar todas las instancias extract"(me,i-have lots. of]punctuation
y almacenar el índice de cada una en una lista.
Entonces, digamos que este trozo de cadena estaba al principio y en medio de la cadena más grande, ambos se encontrarían y sus índices se agregarían al List
. y el List
contendría 0
y el otro índice sea lo que sea.
He estado jugando y string.IndexOf
hace casi lo que estoy buscando, y he escrito algo de código, pero no funciona y no he podido averiguar exactamente qué está mal:
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
= La listasource
= La cuerda grande
¿Alguna idea mejor?
yield
hará que el código sea "perezoso". No recopilará todos los índices en una lista en memoria dentro del método. El tipo de efecto práctico que tiene sobre el rendimiento depende de muchos factores.value.Length
es posible que se pierda las coincidencias anidadas! Ejemplo: "¡Esta es una prueba de coincidencia NestedNestedNested!" con la coincidencia de "NestedNested" encontrará solo un índice, pero no el anidado. Para solucionar esto, simplemente agregue un+=1
bucle en lugar de+=value.Length
.¿Por qué no usas la clase RegEx incorporada?
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; } }
Si necesita reutilizar la expresión, compílela y almacénela en algún lugar. Cambie el parámetro matchString por un matchExpression de Regex en otra sobrecarga para el caso de reutilización.
fuente
indexes
? No está definido en ninguna parte.usando LINQ
public static IEnumerable<int> IndexOfAll(this string sourceString, string subString) { return Regex.Matches(sourceString, subString).Cast<Match>().Select(m => m.Index); }
fuente
Versión pulida + estuche ignorando soporte:
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(); }
fuente
Se podría hacer en una complejidad de tiempo eficiente utilizando el algoritmo KMP en O (N + M) donde N es la longitud de
text
y M es la longitud depattern
.Esta es la implementación y el uso:
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; }
y este es un ejemplo de cómo usarlo:
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 } }
fuente
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; }
Llámalo así:
List<int> list = GetPositions("bob is a chowder head bob bob sldfjl", "bob"); // list will contain 0, 22, 26
fuente
Hola buena respuesta de @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--; } }
Pero esto cubre casos de pruebas como AOOAOOA donde subcadena
son AOOA y AOOA
Salida 0 y 3
fuente
Sin Regex, usando el tipo de comparación de cadenas:
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)
Esto devuelve {3,8,19,22}. El patrón vacío coincidiría con todas las posiciones.
Para múltiples patrones:
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))
Esto devuelve {3, 8, 19, 22, 15, 16}
fuente
@csam es correcto en teoría, aunque su código no cumplirá y se puede refractar a
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; }
fuente
Noté que al menos dos soluciones propuestas no manejan resultados de búsqueda superpuestos. No revisé el marcado con la marca de verificación verde. Aquí hay uno que maneja resultados de búsqueda superpuestos:
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; }
fuente
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; }
fuente
Según el código que he usado para encontrar múltiples instancias de una cadena dentro de una cadena más grande, su código se vería así:
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++; }
fuente
indexOf
devuelve -1 y elindex++
. Usaría awhile (true)
con abreak;
si el resultado deIndexOf
es -1.Encontré este ejemplo y lo incorporé a una función:
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(); }
Devoluciones:
53 encontrado en la posición 2
78 encontrado en la posición 4
78 encontrado en la posición 7
57 no está en 153786
fuente