Получить индекс n-го вхождения строки? - PullRequest
97 голосов
/ 09 октября 2008

Если я не пропустил очевидный встроенный метод, какой самый быстрый способ получить n вхождение строки в строке?

Я понимаю, что мог бы зациклить метод IndexOf , обновляя его начальный индекс на каждой итерации цикла. Но делать это таким образом мне кажется расточительным.

Ответы [ 8 ]

102 голосов
/ 09 октября 2008

Вы действительно можете использовать регулярное выражение /((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 к исходному решению, чтобы разрешить поиск символов, которые имеют особое значение для механизма регулярных выражений.

51 голосов
/ 09 октября 2008

Это в основном то, что вам нужно сделать - или, по крайней мере, это самое простое решение. Все, что вы «потеряете», это стоимость n вызовов методов - вы на самом деле не будете проверять ни одного случая дважды, если подумаете об этом. (IndexOf вернется, как только найдет совпадение, и вы продолжите движение с того места, где оно остановилось.)

19 голосов
/ 22 марта 2011

Это в основном то, что вам нужно сделать - или, по крайней мере, это самое простое решение. Все, что вы «потеряете», это стоимость 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));
        }
    }
}
14 голосов
/ 07 октября 2009
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;
}
1 голос
/ 08 августа 2018

После некоторого тестирования это, кажется, самое простое и эффективное решение

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;
        }
1 голос
/ 13 мая 2014

Может быть, было бы неплохо поработать с методом String.Split() и проверить, находится ли запрошенное вхождение в массиве, если вам не нужен индекс, но значение в индексе

0 голосов
/ 19 августа 2018

System.ValueTuple ftw:

var index = line.Select((x, i) => (x, i)).Where(x => x.Item1 == '"').ElementAt(5).Item2;

написание функции из домашнего задания

0 голосов
/ 08 июля 2012

Это может сделать это:

Console.WriteLine(str.IndexOf((@"\")+2)+1);
...