[英]Get the index of the nth occurrence of a string?
您真的可以使用正則表達式
\/((s).*?){n}\/<\/code>來搜索第 n 次出現的子字符串
s<\/code> 。
在 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;
}
}
這基本上就是你需要做的——或者至少,這是最簡單的解決方案。 您“浪費”的只是 n 次方法調用的成本 - 如果您考慮一下,您實際上不會檢查任何情況兩次。 (IndexOf 將在找到匹配項后立即返回,並且您將從中斷處繼續。)
"這基本上就是你需要做的——或者至少,這是最簡單的解決方案。 您“浪費”的只是 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));
}
}
}
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;
}
經過一些基准測試,這似乎是最簡單和最有效的解決方案
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;
}
也許使用String.Split()<\/code>方法並檢查請求的事件是否在數組中也會很好,如果您不需要索引,但索引處的值
"
System.ValueTuple ftw:
var index = line.Select((x, i) => (x, i)).Where(x => x.Item1 == '"').ElementAt(5).Item2;
寫一個函數是作業
托德的回答可以稍微簡化。
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));
}
}
或者像這樣的 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;
}
在這里,我再次 go:另一個真正來自您的基准答案 :-) 再次基於出色的BenchmarkDotNet package(如果您認真對待基准 do.net 代碼,請使用此包)。
這篇文章的動機有兩個:PeteT(最初提出這個問題的人)想知道在循環中使用String.IndexOf
改變startIndex
參數來查找字符的第 n 次出現似乎很浪費,而事實上,這是最快的方法,並且因為某些答案使用的正則表達式要慢一個數量級(並且在我看來,在這種特定情況下,甚至沒有增加可讀性)。
這是我最終在我的字符串擴展庫中使用的代碼(這不是這個問題的新答案,因為其他人已經在這里發布了語義相同的代碼,我不認為它是功勞)。 這是最快的方法(甚至可能包括不安全的變體——稍后會詳細介紹):
public static int IndexOfNth(this string str, char ch, int nth, int startIndex = 0) {
if (str == null)
throw new ArgumentNullException("str");
var idx = str.IndexOf(ch, startIndex);
while (idx >= 0 && --nth > 0)
idx = str.IndexOf(ch, startIndex + idx + 1);
return idx;
}
我已經將此代碼與其他兩種方法進行了基准測試,結果如下:
基准測試方法是:
[Benchmark]
public int FindNthRegex() {
Match m = Regex.Match(text, "((" + Regex.Escape("z") + ").*?){" + Nth + "}");
return (m.Success)
? m.Groups[2].Captures[Nth - 1].Index
: -1;
}
[Benchmark]
public int FindNthCharByChar() {
var occurrence = 0;
for (int i = 0; i < text.Length; i++) {
if (text[i] == 'z')
occurrence++;
if (Nth == occurrence)
return i;
}
return -1;
}
[Benchmark]
public int FindNthIndexOfStartIdx() {
var idx = text.IndexOf('z', 0);
var nth = Nth;
while (idx >= 0 && --nth > 0)
idx = text.IndexOf('z', idx + 1);
return idx;
}
FindNthRegex
方法是其中較慢的一個,比最快的方法多花費一個(或兩個)數量級的時間。 FindNthByChar
字符串中的每個char
並對每個匹配項進行計數,直到找到第 n 次出現。 FindNthIndexOfStartIdx
使用這個問題的開場白所建議的方法,實際上,這與我多年來一直使用的方法相同,而且它是所有方法中最快的。
為什么它比FindNthByChar
? 這是因為 Microsoft 竭盡全力在 do.net 框架中使字符串操作盡可能快。 他們已經做到了:他們做得非常出色! 我在 CodeProject 文章中對 do.net 中的字符串操作進行了更深入的研究,該文章試圖找到從字符串中刪除所有空格的最快方法:
在那里您會發現為什么 do.net 中的字符串操作如此之快,以及為什么通過編寫我們自己版本的框架字符串操作代碼(例如string.IndexOf
、 string.Split
、 string.Replace
等)
我使用的完整基准測試代碼如下(它是一個 do.net6 控制台程序):
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text;
using System.Text.RegularExpressions;
var summary = BenchmarkRunner.Run<BenchmarkFindNthChar>();
public class BenchmarkFindNthChar
{
const string BaseText = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
[Params(100, 1000)]
public int BaseTextRepeatCount { get; set; }
[Params(500)]
public int Nth { get; set; }
private string text;
[GlobalSetup]
public void BuildTestData() {
var sb = new StringBuilder();
for (int i = 0; i < BaseTextRepeatCount; i++)
sb.AppendLine(BaseText);
text = sb.ToString();
}
[Benchmark]
public int FindNthRegex() {
Match m = Regex.Match(text, "((" + Regex.Escape("z") + ").*?){" + Nth + "}");
return (m.Success)
? m.Groups[2].Captures[Nth - 1].Index
: -1;
}
[Benchmark]
public int FindNthCharByChar() {
var occurrence = 0;
for (int i = 0; i < text.Length; i++) {
if (text[i] == 'z')
occurrence++;
if (Nth == occurrence)
return i;
}
return -1;
}
[Benchmark]
public int FindNthIndexOfStartIdx() {
var idx = text.IndexOf('z', 0);
var nth = Nth;
while (idx >= 0 && --nth > 0)
idx = text.IndexOf('z', idx + 1);
return idx;
}
}
這可能會做到:
Console.WriteLine(str.IndexOf((@"\")+2)+1);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.