字符串的 IndexOfAll 函数,带计数参数

IndexOfAll function for string, with count parameter

我想编写一个 IndexOfAll 函数,该函数必须 return 在该字符串中找到的给定模式的所有出现的索引。

该函数有一个 startIndexcount 参数,就像内置 .NET 函数的重载之一:string.IndexOf()

count参数,和内置的.NET函数一样,会代表number/max。要在源字符串中检查的字符位置范围。

问题是,我正在努力思考如何正确计算 count 的值。可能这将是一个非常简单的数学计算,但我不明白,我最终得到 count 的负数值(这就是为什么我有一个 try/catch 块下面的代码,因为错误的计数值计算我最终得到一个异常)...

那么,我如何计算 count 值?。这是我的:

Public Function IndexOfAll(str As String,
                           find As String,
                           startIndex As Integer,
                           count As Integer,
                           comparisonType As StringComparison) As Integer()

    Dim indices As New Collection(Of Integer)()
    Dim index As Integer = str.IndexOf(find, startIndex, count, comparisonType)
    indices.Add(index)

    Do
        startIndex = index + 1
        count = ???
        ' Console.WriteLine($"idx {index}, start {startIndex}, count {count}")
        Try
            index = str.IndexOf(find, startIndex, count, comparisonType)
            indices.Add(index)
        Catch ex As Exception
            Exit Do
        End Try
    Loop

    Return indices.ToArray()

End Function

注意: 调用内置 string.IndexOf() 函数对我来说没问题,至少现在是这样。我不会假装尝试实现复杂的字符串解析算法(可能会被破坏)来执行与 string.IndexOf() 完全相同的操作,但只需几秒或几毫秒,具体取决于字符串的大小。

找到结束索引(startIndex + count),然后减去新的开始索引。

您还需要在将索引添加到列表之前检查它。

Public Function IndexOfAll(
    str As String,
    find As String,
    startIndex As Integer,
    count As Integer,
    comparisonType As StringComparison) As Integer()
    
    Dim endIndex As Integer = startIndex + count
    Dim indices As New Collection(Of Integer)()
    Dim index As Integer = str.IndexOf(find, startIndex, count, comparisonType)
    
    Do While index <> -1
        indices.Add(index)
        startIndex = index + 1
        count = endIndex - startIndex
        index = str.IndexOf(find, startIndex, count, comparisonType)
    Loop
    
    Return indices.ToArray()
End Function

可以用 LINQ 完成 one-liner

Public Function IndexOfAll(source As String, find As String, startIndex As Integer, count As Integer, comparisonType As StringComparison) As IEnumerable(Of Integer)
    Return source.
        Skip(startIndex).
        Take(source.Length - find.Length - startIndex + 1).
        Select(Function(c, i) source.IndexOf(find, i + startIndex, count, comparisonType)).
        Where(Function(i) i >= 0)
End Function

Skip and Take 的目的是只查看我们将开始比较的第一个字符。 Select 获取字符及其索引。但是因为我们跳过了,所以我们需要将 startIndex 添加回 IndexOf 中。 Select 然后 returns 字符位置。其中 returns 只有那些非负数的字符位置。