105

除非我遗漏了一个明显的内置方法,否则在字符串中第n次出现字符串的最快方法是什么?

我意识到我可以通过在循环的每次迭代中更新它的起始索引来循环IndexOf方法。但这样做对我来说似乎很浪费。

4

10 回答 10

110

您真的可以使用正则表达式/((s).*?){n}/来搜索第 n 次出现的 substring 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到原始解决方案中,以允许搜索对正则表达式引擎具有特殊含义的字符。

于 2008-10-09T14:01:58.250 回答
53

这基本上就是你需要做的——或者至少,这是最简单的解决方案。您“浪费”的只是 n 次方法调用的成本 - 如果您考虑一下,您实际上不会检查任何情况两次。(IndexOf 将在找到匹配项后立即返回,并且您将从中断处继续。)

于 2008-10-09T10:26:51.290 回答
20

这基本上就是你需要做的——或者至少,这是最简单的解决方案。您“浪费”的只是 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));
        }
    }
}
于 2011-03-22T03:34:26.223 回答
15
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;
}
于 2009-10-07T04:39:44.747 回答
2

经过一些基准测试,这似乎是最简单和最有效的解决方案

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;
        }
于 2018-08-08T19:58:14.913 回答
1

也许使用String.Split()方法并检查请求的事件是否在数组中也会很好,如果您不需要索引,但索引处的值

于 2014-05-13T09:08:10.327 回答
1

System.ValueTuple ftw:

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

写一个函数是作业

于 2018-08-19T00:07:26.703 回答
0

托德的回答可以稍微简化。

using System;

static class MainClass {
    private static int IndexOfNth(this string target, string substring,
                                       int seqNr, int startIdx = 0)
    {
        if (seqNr < 1)
        {
            throw new IndexOutOfRangeException("Parameter 'nth' must be greater than 0.");
        }

        var idx = target.IndexOf(substring, startIdx);

        if (idx < 0 || seqNr == 1) { return idx; }

        return target.IndexOfNth(substring, --seqNr, ++idx); // skip
    }

    static void Main () {
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 1));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 2));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 3));
        Console.WriteLine ("abcbcbcd".IndexOfNth("bc", 4));
    }
}

输出

1
3
5
-1
于 2019-08-16T15:39:55.610 回答
0

或者像这样的 do while 循环

 private static int OrdinalIndexOf(string str, string substr, int n)
    {
        int pos = -1;
        do
        {
            pos = str.IndexOf(substr, pos + 1);
        } while (n-- > 0 && pos != -1);
        return pos;
    }
于 2019-12-21T21:48:47.427 回答
-4

这可能会做到:

Console.WriteLine(str.IndexOf((@"\")+2)+1);
于 2012-07-08T07:05:54.497 回答