获取字符串第 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) => (x, i)).Where(x => 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"。如果match
是null
,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 次出现的索引?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 2 个不同的模式获取第 n 次出现之间的所有字符串