C# 获取字符串第 n 次出现的索引?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/186653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:07:13  来源:igfitidea点击:

Get the index of the nth occurrence of a string?

c#.netstringindexingindexof

提问by PeteT

Unless I am missing an obvious built-in method, what is the quickest way to get the nth occurrence of a string within a string?

除非我缺少一个明显的内置方法,否则获取字符串中第n次出现的最快方法是什么?

I realize that I could loop the IndexOfmethod by updating its start index on each iteration of the loop. But doing it this way seems wasteful to me.

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

采纳答案by Jon Skeet

That's basically what you need to do - or at least, it's the easiest solution. All you'd be "wasting" is the cost of n method invocations - you won't actually be checking any case twice, if you think about it. (IndexOf will return as soon as it finds the match, and you'll keep going from where it left off.)

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

回答by Alexander Prokofyev

You really could use the regular expression /((s).*?){n}/to search for n-th occurrence of substring s.

您真的可以使用正则表达式/((s).*?){n}/来搜索第 n 次出现的 substring s

In C# it might look like this:

在 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;
    }
}

Note:I have added Regex.Escapeto original solution to allow searching characters which have special meaning to regex engine.

注意:我已添加Regex.Escape到原始解决方案中,以允许搜索对正则表达式引擎具有特殊含义的字符。

回答by Schotime

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;
}

or in C# with extension methods

或在 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;
}

回答by Tod Thomson

That's basically what you need to do - or at least, it's the easiest solution. All you'd be "wasting" is the cost of n method invocations - you won't actually be checking any case twice, if you think about it. (IndexOf will return as soon as it finds the match, and you'll keep going from where it left off.)

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

Here is the recursive implementation (of the above idea) as an extension method, mimicing the format of the framework method(s):

这是作为扩展方法的递归实现(上述想法的),模仿框架方法的格式:

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);
}

Also, here are some (MBUnit) unit tests that might help you (to prove it is correct):

此外,这里有一些 (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));
        }
    }
}

回答by Sameer Shaikh

This might do it:

这可能会做到:

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

回答by user3227623

Maybe it would also be nice to work with the String.Split()Method and check if the requested occurrence is in the array, if you don't need the index, but the value at the index

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

回答by ShadowBeast

After some benchmarking, this seems to be the simplest and most effcient solution

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

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;
        }

回答by Matthias

System.ValueTuple ftw:

System.ValueTuple ftw:

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

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

writing a function from that is homework

写一个函数是家庭作业

回答by seron

Tod's answer can be simplified somewhat.

托德的回答可以简化一些。

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));
    }
}

Output

输出

1
3
5
-1

回答by xFreeD

Or something like this with the do while loop

或者像这样的 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;
    }