2009-12-25 52 views
33

我正在尋找快速安全的方式在流上應用正則表達式。在流上應用正則表達式?

我在網上找到了一些關於將每個緩衝區轉換爲字符串的例子,然後在字符串上應用Regex

這種方法有兩個問題:

  • 性能:轉換爲字符串和GC'ing字符串是如果有對數據流應用Regex更原始的方式可避免的時間,CPU和肯定浪費。
  • Regex支持:Regex圖案有時只能匹配如果兩個緩衝器組合在一起(緩衝存儲器1與匹配的第一部分結束時,和緩衝液2點開始與匹配的第二部分)。轉換爲字符串的方式無法處理這種類型的本地匹配,我必須提供更多信息,例如模式可以匹配的最大長度,這根本不支持+和* regex標誌,並且永遠不會支持(無限匹配長度)。

因此,轉換爲字符串的方式並不快,並且不完全支持Regex

是否有任何方式/圖書館可用於在Streams上應用Regex而不轉換爲字符串和完整的Regex支持?

+0

那麼,爲什麼你不能等到你收到的所有數據? – ChaosPandion 2009-12-25 23:33:05

+0

根據我的經驗,通常會阻礙性能的正則表達式,而不是轉換和GC'ing字符串。除非您的匹配非常複雜,否則我建議您爲匹配而不是正則表達式創建您自己的流掃描器。 但是,您應該使用正則表達式對其進行基準測試,以確保您處於正確的軌道上。 – 2009-12-26 00:04:41

+2

@ChaosPandion:如果流是一個大文件,我不會將所有的gigas加載到內存中,尤其是不在utf-16中(在.net中的內存中的字符串編碼)。 如果流來自互聯網,我希望能夠在接收到的所有數據(IE HTML解析器,在下載頁面的其餘部分之前顯示下載的部分)之前掃描它。 – DxCK 2009-12-26 00:09:09

回答

2

也許這篇文章可以幫助嗎?雖然我想這可能是「互聯網上的東西」,但你發現這並不是幫助。

Building a Regular Expression Stream Search with the .NET Framework

+0

此解決方案通常不起作用。如果您在調用StreamSearchExpression構造函數之前不知道匹配的最大長度和可能匹配之間的最小距離,將無法找到所有匹配項(將可能匹配項之間的最小距離設置爲1會使算法非常低效)。另外,這個解決方案將流轉換爲引擎之下的字符串(在構造函數中傳入的最大匹配長度),這是psubsee2003試圖避免的。 – mheyman 2013-04-01 11:10:17

+0

儘管這個鏈接可能回答這個問題,但最好在這裏包含答案的重要部分,並提供供參考的鏈接。如果鏈接頁面更改,則僅鏈接答案可能會失效。 - [來自評論](/ review/low-quality-posts/18810911) – 2018-02-13 16:04:12

0

看來,你就會知道比賽的開始和結束的分隔符,你試圖讓,正確嗎? (即[,]或開始,結束等)。當你的流中的數據進入,然後在分隔符之間創建一個子串並對這些分隔符做進一步處理時,搜索這些分隔符是否合理?

我知道這幾乎是同樣的事情滾動您自己,但是這將是一個更具體的目的,甚至可以,因爲它涉及到處理它。

在這種情況下正則表達式的問題是他們基於比賽工作,所以你只能匹配你的輸入量。如果你有一個流,你必須閱讀所有的數據才能獲得所有的匹配(空間/時間約束問題),嘗試匹配字符在一個時間(很無用),大塊匹配(再次,有些東西可能很容易錯過),或者產生感興趣的字符串,如果它們符合您的標準,可以在其他地方運出以供進一步處理。

0

您可以爲StreamReader添加一個額外的方法(例如,單可以被用於該目的):

private StringBuilder lineBuilder; 
    public int RegexBufferSize 
    { 
     set { lastRegexMatchedLength = value; } 
     get { return lastRegexMatchedLength; } 
    } 
    private int lastRegexMatchedLength = 0; 

    public virtual string ReadRegex(Regex regex) 
    { 
     if (base_stream == null) 
      throw new ObjectDisposedException("StreamReader", "Cannot read from a closed RegexStreamReader"); 

     if (pos >= decoded_count && ReadBuffer() == 0) 
      return null; // EOF Reached 

     if (lineBuilder == null) 
      lineBuilder = new StringBuilder(); 
     else 
      lineBuilder.Length = 0; 

     lineBuilder.Append(decoded_buffer, pos, decoded_count - pos); 
     int bytesRead = ReadBuffer(); 

     bool dataTested = false; 
     while (bytesRead > 0) 
     { 
      var lineBuilderStartLen = lineBuilder.Length; 
      dataTested = false; 
      lineBuilder.Append(decoded_buffer, 0, bytesRead); 

      if (lineBuilder.Length >= lastRegexMatchedLength) 
      { 
       var currentBuf = lineBuilder.ToString(); 
       var match = regex.Match(currentBuf, 0, currentBuf.Length); 
       if (match.Success) 
       { 
        var offset = match.Index + match.Length; 
        pos = 0; 
        decoded_count = lineBuilder.Length - offset; 
        ensureMinDecodedBufLen(decoded_count); 
        lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count); 
        var matchedString = currentBuf.Substring(match.Index, match.Length); 
        return matchedString; 
       } 
       else 
       { 
        lastRegexMatchedLength *= (int) 1.1; // allow for more space before attempting to match 
        dataTested = true; 
       } 
      } 

      bytesRead = ReadBuffer(); 
     } 

     // EOF reached 

     if (!dataTested) 
     { 
      var currentBuf = lineBuilder.ToString(); 
      var match = regex.Match(currentBuf, 0, currentBuf.Length); 
      if (match.Success) 
      { 
       var offset = match.Index + match.Length; 
       pos = 0; 
       decoded_count = lineBuilder.Length - offset; 
       ensureMinDecodedBufLen(decoded_count); 
       lineBuilder.CopyTo(offset, decoded_buffer, 0, decoded_count); 
       var matchedString = currentBuf.Substring(match.Index, match.Length); 
       return matchedString; 

      } 
     } 
     pos = decoded_count; 

     return null; 
    } 

在上述方法中,下面的VARS被使用:

  1. decoded_buffer:包含/將包含數據讀
  2. POS炭緩衝:包含未處理數據的陣列中的偏移量
  3. decoded_count:包含讀取數據的緩衝區中的最後一個元素
  4. RegexBufferSize:最大大小的正則表達式輸入之前匹配發生。

方法ReadBuffer()需要從流讀取數據。 方法ensureMinDecodedBufLen()需要確保decode_buffer足夠大。

當調用該方法中,通過需要針對要匹配的正則表達式。

4

英特爾最近開源下BSD許可證hyperscan庫。這是一個高性能的非回溯式基於NFA的正則表達式引擎。

特點:對輸入數據和同時多模式匹配的流工作的能力。最後一個不同於(pattern1|pattern2|...)的方法,它實際上併發地匹配模式。

它還採用英特爾SIMD指令集SSE4.2一樣,AVX2和BMI。 設計和工作說明的總結可以找到here。 它也有很大的開發者的reference guide有很多解釋以及性能和使用方面的考慮。 小article關於在野外使用它(俄語)。

相關問題