Получить индекс n-го вхождения строки?
Если я не пропустил очевидный встроенный метод, каков самый быстрый способ получить nth вхождение строки в строку?
Я понимаю, что я мог бы цикл IndexOf метод путем обновления его начального индекса на каждой итерации цикла. Но делать это таким образом кажется мне расточительным.
8 ответов:
Это в основном то, что вам нужно сделать - или, по крайней мере, это самое простое решение. Все, что вы будете "тратить", - это стоимость N вызовов метода-вы на самом деле не будете проверять ни одно дело дважды, если вы подумаете об этом. (IndexOf вернется, как только он найдет совпадение, и вы будете продолжать идти с того места, где он остановился.)
вы действительно могли бы использовать регулярное выражение
/((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
к оригинальному решению, чтобы разрешить поиск символов, которые имеют особое значение для механизма регулярных выражений.
это в основном то, что вам нужно сделать - или, по крайней мере, это самое простое решение. Все, что вы будете "тратить", - это стоимость N вызовов метода-вы на самом деле не будете проверять ни одно дело дважды, если вы подумаете об этом. (IndexOf вернется, как только он найдет совпадение, и вы будете продолжать идти с того места, где он остановился.)
вот рекурсивная реализация (выше идея) как метод расширения, имитирующий формат фреймворка метод(ы):
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)); } } }
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; }
или в C# методы расширения
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; }
может быть, было бы приятно работать с
String.Split()
метод и проверить, если запрошенное вхождение находится в массиве, если вам не нужен индекс, но значение в индексе
после некоторого бенчмаркинга это кажется самым простым и эффективным решением
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; }