2017-07-31 39 views
1

我有一個正則表達式用空字符串替換非打印字符。 (在XML文檔中不支持的那些)C# - 在Regex中使用matchTimeout參數。替換

傳入數據的大小非常大,所以如果這個Replace花費的時間超過幾個毫秒,我想要取消它並返回原始字符串。

下面是我的代碼,但我似乎無法擊中catch塊,即使我提供了1ms的Timespan。來自stopwatch的日誌顯示花費了超過10毫秒。

我在這裏做錯了什麼?

只有當它在給定的時間範圍內沒有找到匹配時,它才能正常工作嗎?

什麼是最好的測試方法?

更新 - 我用一個沒有任何非打印字符的大文件(4 MB)測試了下面的正則表達式。 Regex花了79毫秒,但沒有例外。

private static string CleanUpNonPrintableCharacters(string incomingString) 
    { 
     var stopWatch = new Stopwatch(); 
     try 
     { 
      stopWatch.Start(); 
      var timeSpan = TimeSpan.FromMilliseconds(1); 

      var cleanedUpString = Regex.Replace(incomingString, @"[\u0000-\u0008\u000B\u000C\u000E-\u001F]", string.Empty, RegexOptions.None, timeSpan); 

      stopWatch.Stop(); 
      Console.Log(stopWatch.ElapsedMilliseconds); 
      //Above was 79 ms on a file that doesn't have a match, yet no exception was thrown 

      if (cleanedUpString.Length < incomingString.Length) 
      { 
       //do some logging 
      } 
      return cleanedUpString; 
     } 
     catch (RegexMatchTimeoutException ex) 
     { 
      //do some logging 
      return incomingString; 
     } 
     finally 
     { 
      //stopWatch.Stop(); 
      //log elapsed 
     } 
    } 
+0

我想,你的'stopWatch.Stop();'的代碼應該是因爲它似乎只是更換後的代碼在秒錶停止之前還有其他操作。 –

+0

您可以使用委託,然後跟蹤已用時間。如果>限制,可能會在代表內部_throw_內部。不知道這一點(如果正則表達式引擎展開並退出).. – sln

+0

的確,我可以通過用'Task'包裝上述util方法並使用'Timeout'發出'CancellationToken'來解決上述問題,但不知何故,我覺得這應該開箱即用,因爲'Regex.Replace'提供超時選項。 @AkashKC,我用我最近的測試結果更新了我的問題。 – Ren

回答

2

The manual states

的matchTimeout參數指定多久匹配方法的模式應該嘗試在超時前找到匹配。設置超時間隔可防止依賴於過度回溯的正則表達式在處理包含近乎匹配的輸入時停止響應。

我假設它發現下一場比1ms快的比賽,但有很多比賽,所以它加起來。

+1

我想到了同樣的情況,但我只是用一個包含4MB lorem ipsum文本的文件來測試它。沒有任何匹配,正則表達式需要79ms,但沒有例外。我會嘗試'Regex.IsMatch',看看是否能捕捉到任何東西。 – Ren

+1

@Ren在超時[這裏](https://ideone.com/oqq8hV)上玩了一下。超時確實會在每場比賽之間重置。一些猜測如下...掃描一個簡單的char類的4MB字符串可能非常快(ms的數量級),並且如果優化可以在O(1)時間複雜度下完成。也許替換調用正在做一些優化或首先,超時不適用。限額可能太低了嗎?你可以嘗試更大的字符串或更復雜的正則表達式(帶回溯)?或者,如果在正則表達式中沒有回溯,則禁用超時。 – Qtax

2

據我所知,timeout只用於匹配模式,而不是用超時替換匹配字符。

如果你看看Regex source code,這裏是超時已被用來找出匹配的代碼:

match = runner.Scan(this, input, beginning, beginning + length, 
       startat, prevlen, quick, internalMatchTimeout);.// runner is RegexRunner instance 

所以,Regex.Replace包括匹配方法和更換方法。在你的情況下,匹配速度非常快,所以它不會拋出RegexMatchTimeoutException異常,但替換給定匹配的方法似乎很慢。

我沒有在我結束測試,但你可以調用僅Match方法測試,看看結果