Получить индекс n-го вхождения строки?
Если мне не хватает очевидного встроенного метода, каков самый быстрый способ получить n- е вхождение строки внутри строки?
Я понимаю, что я мог бы закодировать метод IndexOf , обновив свой индекс начала на каждой итерации цикла. Но делать это так кажется мне расточительным.
- Получить версию java из командного файла
- Эффективный способ сравнения строк версии в Java
- Почему class String объявлен окончательным в Java?
- Как объявить и использовать логические переменные в сценарии оболочки?
- Найти строку текста в элементе и обернуть вокруг него tags span
- Преобразование строки в байтовый массив в C #
- Команда не обнаружила ошибку в присвоении переменной Bash
- Проверьте, соответствует ли строка регулярному выражению в сценарии Bash
- C / C ++: оптимизация указателей на строковые константы
- Определите, является ли строка C допустимым int в C
- Получить n-й символ строки в языке программирования Swift
- Как использовать «.» Как разделитель с String.split () в java
- Разделить строку с точкой в качестве разделителя
Это в основном то, что вам нужно сделать – или, по крайней мере, это самое простое решение. Все, что вы «тратите впустую», это стоимость 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( () => 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)).Where(x => x.Item1 == '"').ElementAt(5).Item2;
запись функции из этой домашней работы
Это может сделать это:
Console.WriteLine(str.IndexOf((@"\")+2)+1);