2010-11-30 58 views
5

我可以用字符串替换字符的速度有多快?VB.NET中字符串的替换

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

现在,我开始对这个小小的冒险指数从this MSDN link这是我从不同的岗位从这个网站上发现。

我们当前使用的方法......在一天的开始...正在使用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进行循环。

所以我的问题是:我可以做得更快吗?我错过了什么?

+0

如果你必须使用C#的选项,你也许可以写出不安全的代码,一个不怀好意的快捷功能。 – Juliet 2010-11-30 22:41:17

+0

我曾想知道这是否可能,但很快就将这个选项计算出来了,因为我得到的参数与它们一起工作......哦, – Tim 2010-12-01 03:07:05

回答

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遍历字符最多两次(一次遍历,一次当字符串构造函数复制char数组时),使这个函数成为O(n)。

0

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

+0

StringBuilder的replace()函数是我整天试过的最慢的函数。当通过240条日志消息进行处理时,StringBuilder比处理来自我的OP的消息的最后一个方式慢1.8毫秒。 – Tim 2010-11-30 22:20:40

+0

“这取决于”在这里是正确的答案。如果没有真正的性能分析,你无法真正保证StringBuilder的速度更快。 – Juliet 2010-11-30 22:22:22

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 

请注意,您的基准,将在很大程度上取决于那种你在它扔串的,所以一定要确保你使用最有代表性的样本(它应该是一个好大小的样本)是可能的。

+0

我用不同的方式使用.IndexOf()和.Contains()而没有获得任何结果,通常大约慢一到两毫秒。我将尝试使用您的编号2和3进行更多测试...我将回传结果 – Tim 2010-11-30 22:29:19

+0

@Tim - 请参阅我的更新回答 – 2010-11-30 22:33:34

0

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

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