Получить индекс n-го вхождения строки?
Вопрос
Если у меня нет очевидного встроенного метода, какой самый быстрый способ получить n вхождение строки в строке?
Я понимаю, что могу зациклить IndexOf , обновляя свой начальный индекс на каждой итерации цикла. Но делать это таким образом мне кажется расточительным.
Решение
Это в основном то, что вам нужно сделать - или, по крайней мере, это самое простое решение. Все, что вы будете "тратить" это стоимость 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;
}
System.ValueTuple ftw:
var index = line.Select ((x, i) = > (x, i)). Где (x = & xt. x.Item1 == '"'). ElementAt (5). Элемент2; код>
написание функции из этого - домашнее задание
Это может сделать это:
Console.WriteLine(str.IndexOf((@"\")+2)+1);