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

Posted

技术标签:

【中文标题】获取字符串第 n 次出现的索引?【英文标题】:Get the index of the nth occurrence of a string? 【发布时间】:2010-09-16 05:52:18 【问题描述】:

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

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

【问题讨论】:

我会为此使用正则表达式,然后您必须以最佳方式匹配字符串中的字符串。这是我们都应该尽可能使用的漂亮 DSL 之一。 An example 在 VB.net 中的代码与 C# 中的代码几乎相同。 我会花很多钱在正则表达式版本上,它比“保持循环并执行简单的 String.IndexOf”要难得多。正则表达式有它们的位置,但不应该在存在更简单的替代方案时使用。 类似:***.com/a/9908392/1305911 【参考方案1】:

或者类似这样的 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;
    

【讨论】:

【参考方案2】:

Tod 的回答可以稍微简化一下。

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

【讨论】:

【参考方案3】:

System.ValueTuple ftw:

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

写一个函数是功课

【讨论】:

【参考方案4】:

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

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;
        

【讨论】:

【参考方案5】:

您确实可以使用正则表达式/((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 添加到原始解决方案中,以允许搜索对正则表达式引擎具有特殊意义的字符。

【讨论】:

你应该转义value吗?就我而言,我正在寻找一个点msdn.microsoft.com/en-us/library/… 如果目标字符串包含换行符,则此正则表达式不起作用。你能修好吗?谢谢。 如果没有第 N 个匹配项似乎会锁定。我需要将逗号分隔的值限制为 1000 个值,当 csv 的值较少时,这会挂起。所以@Yogesh - 可能不是一个很好的接受答案。 ;) 使用this answer 的变体(有一个字符串到字符串版本here)和changed the loop to stop at nth count。 尝试在\上搜索,传入的值为“\\”,匹配字符串在regex.match函数之前如下所示:(().*?)2。我收到此错误:解析“(()。*?)2” - 不够)。什么是正确查找反斜杠且不出错的格式? 对不起,但有一个小小的批评:正则表达式解决方案不是最理想的,因为那时我必须第 n 次重新学习正则表达式。使用正则表达式时,代码本质上更难阅读。【参考方案6】:

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

【讨论】:

【参考方案7】:

这基本上就是您需要做的 - 或者至少,这是最简单的解决方案。你“浪费”的只是 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));
        
    

【讨论】:

我根据 Weston 的出色反馈更新了我的格式和测试用例(感谢 Weston)。【参考方案8】:

这可能会做到:

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

【讨论】:

我不明白这将如何工作。您能否简要解释一下这是做什么的?【参考方案9】:
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;

【讨论】:

如果我没记错的话,如果要匹配的字符串从位置 0 开始,则此方法会失败,可以通过将 index 初始设置为 -1 来纠正此问题。 您可能还想检查 null 或空字符串并匹配,否则它会抛出但这是一个设计决定。 感谢@PeterMajeed - 如果"BOB".IndexOf("B") 返回0,那么IndexOfOccurence("BOB", "B", 1) 的这个函数也应该如此 你的可能是最终的解决方案,因为它既有扩展功能,又避免了正则表达式和递归,这两者都会降低代码的可读性。 @tdyen 实际上,如果IndexOfOccurence 不检查s 是否为null,代码分析将发出"CA1062: Validate arguments of public methods"。如果matchnull,String.IndexOf (String, Int32) 将抛出ArgumentNullException【参考方案10】:

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

【讨论】:

我想你是对的,它似乎应该有一个内置的方法,但我敢肯定这是一个常见的情况。 真的吗?我不记得在大约 13 年的 Java 和 C# 开发中曾经做过。这并不意味着我真的从来没有这样做过——只是不够经常记住。 说到Java,我们有StringUtils.ordinalIndexOf()。具有所有 Linq 和其他出色功能的 C# 只是没有对此的内置支持。是的,如果您正在处理解析器和标记器,那么获得它的支持是非常必要的。 @Annie:你说“我们有”——你的意思是在 Apache Commons 中吗?如果是这样,您可以像为 Java 一样轻松地为 .NET 编写自己的第三方库……所以这并不是 Java 标准库所具有的 .NET 所没有的东西。当然,在 C# 中,您可以将其作为扩展方法添加到 string :)

以上是关于获取字符串第 n 次出现的索引?的主要内容,如果未能解决你的问题,请参考以下文章

如何在字符串中查找第 n 个出现的字符?

如何使用 2 个不同的模式获取第 n 次出现之间的所有字符串

如何在 Hive 中构建正则表达式以获取字符串,直到第 N 次出现分隔符

在java中的字符串中找到第n次出现的子字符串?

如何找到一个项目第n次出现在列表中的索引?

我要获取一个字符串中某个标点第二次出现的位置