6

我可以多快替换字符串中的字符?

所以这个问题的背景是这样的:我们有几个应用程序通过套接字相互通信并与客户的应用程序通信。这些套接字消息包含不可打印的字符(例如 chr(0)),需要将其替换为预先确定的字符串(例如“{Nul}”},因为套接字消息保存在日志文件中。附带说明,不是每条日志消息都需要替换字符。

现在,我从这个 MSDN 链接开始阅读这个小冒险,这个链接是我从这个网站的另一篇文章中找到的。

我们使用的当前方法......在一天开始时......正在使用 StringBuilder 来检查所有可能的替代品,例如......

    Public Function ReplaceSB(ByVal p_Message As String) As String
      Dim sb As New System.Text.StringBuilder(p_Message)

      sb.Replace(Chr(0), "{NUL}")
      sb.Replace(Chr(1), "{SOH}")

      Return sb.ToString
    End Function

现在,正如博客文章指出的那样,将 StringBuilder 排除在外并使用 string.replace 确实会产生更快的结果。(实际上,使用 StringBuilder 是一整天最慢的方法。)

    p_Message = p_Message.Replace(Chr(0), "{NUL}")
    p_Message = p_Message.Replace(Chr(1), "{SOH}")

知道不是每条消息都需要经过这个过程,我认为不必处理那些可能被遗漏的消息会节省时间。因此,我首先使用正则表达式搜索字符串,然后确定是否需要对其进行处理。这与使用 string.replace 大致相同,基本上是节省了不处理所有字符串的时间,但浪费了使用正则表达式检查它们的时间。

然后建议尝试使用一些将它们的索引与旧的和新的匹配的数组,并使用它来处理消息。所以它会是这样的......

Private chrArray() As Char = {Chr(0), Chr(1)}
Private strArray() As String = {"{NUL}", "{SOH}"}

Public Function TestReplace(ByVal p_Message As String) As String
    Dim i As Integer

    For i = 0 To ((chrArray.Length) - 1)
        If p_Message.Contains(chrArray(i).ToString) Then
            p_Message = p_Message.Replace(chrArray(i), strArray(i))
        End If
    Next

    Return p_Message
End Function

到目前为止,这是我发现处理这些消息的最快方法。我已经尝试了各种其他方法来解决这个问题,比如将传入的字符串转换为字符数组并进行比较,还尝试循环遍历字符串而不是 chrArray。

所以我对所有人的问题是:我可以让它更快吗?我错过了什么?

4

5 回答 5

1

您可以通过减少一些查找来加快速度。举个例子:

    If p_Message.Contains(chrArray(i).ToString) Then

.Contains方法是 O(n) 。在最坏的情况下,您将遍历整个字符串中的所有字符而不找到任何内容,因此您希望对数组中的每个字符至少遍历一次,因此它的 O(nm) 其中 n 是您的字符串和 m 是您要替换的字符数。

执行以下操作可能会获得更好的性能(我的 VB-fu 生锈了,尚未经过测试;)):

Private Function WriteToCharList(s as String, dest as List(Of Char))
    for each c as Char in s
        dest.Add(c)
    Next
End Function

Public Function TestReplace(ByVal p_Message As String) As String
    Dim chars as new List(Of Char)(p_Message.Length)

    For each c as Char in p_Message
        Select Case c
            Case Chr(0): WriteToCharList("{NUL}", chars)
            Case Chr(1): WriteToCharList("{SOH}", chars)
            Case Else: chars.Add(c);
        End Select
    Next

    Return New String(chars)
End Function

这将p_Message最多遍历字符两次(一次用于遍历,一次在字符串构造函数复制字符数组时),使此函数成为 O(n)。

于 2010-11-30T22:34:57.083 回答
0

这里有几个一般说明:

  1. 您可以通过使用普通.IndexOf().Contains()搜索来改进搜索功能,因为您只寻找单个字符。
  2. 您可以通过直接从函数返回 StringBuilder 对象并为接受字符串构建器作为输入的其他函数提供重载或在过程中稍后的某个位置调用 .ToString() 来提高总吞吐量(注意:您也可以调用 .ToString () 在已经是字符串的对象上)
  3. 您绝对应该能够通过使用 StringReader/TextReader 进一步提高性能/吞吐量,并继续将所有内容视为不断向上传递的流。

至少你可以这样修改你的最终方法:

Public Function TestReplace(ByVal p_Message As String) As String
    Static chrArray() As Char = {ChrW(0), ChrW(1)}
    Static strArray() As String = {"{NUL}", "{SOH}"}

    Dim rdr As New StringReader(p_Message)
    Dim result As New StringWriter()

    Dim i As Integer
    While (i = rdr.Read()) <> -1
        Dim c As Char = ChrW(i)
        Dim index As Integer = Array.IndexOf(chrArray, c)
        If index >= 0 Then result.Write(strArray(index)) Else result.Write(c)
    End While

    Return result.ToString()
End Function

请注意,您的基准测试很大程度上取决于您使用的字符串类型,因此请确保您使用的是最具代表性的样本(并且它应该是一个合适大小的样本)。

于 2010-11-30T22:21:28.727 回答
0

看看这个例子。它有一些比较这两种方法的基准统计数据。

于 2010-11-30T22:22:07.523 回答
0

StringBuilder提供 .NET 中最快的Replace() 函数。

于 2010-11-30T22:12:32.943 回答
0

这也应该更快:

    Private Shared strList As New Dictionary(Of Char, String)

    Shared Sub New()
        strList.Add(Chr(0), "{NUL}")
        strList.Add(Chr(1), "{SOH}")
    End Sub

    Public Function TestReplace(ByVal p_Message As String) As String
        For Each c As Char In strList.Keys
            If p_Message.IndexOf(c) <> -1 Then
                p_Message = p_Message.Replace(c, strList(c))
            End If
        Next

        Return p_Message
    End Function
于 2010-11-30T22:39:26.423 回答