2012-11-27 27 views
1

使用RegEx時遇到一個奇怪的行爲。有人可以解釋此RegEx行爲嗎?

dataString = "#Name #Location New York #Rating" 
string[] rawValues = Regex.Split(dataString.Trim(), "(^|\\s)+#\\w+"); 

的模式匹配:"#Name", " #Location", " #Rating"(這正是我打算匹配)。
分割回報:["", "", "", " ", "New York", " ", ""]

問題1:cunfusion已經從這裏開始。爲什麼在位置0,1,2有空串?兩場比賽和一場比賽,因爲它在比賽的第一個位置?

但是,這不是奇怪的一部分。

string[] rawValues = Regex.Split(dataString.Trim(), "(\\s|^)+#(\\w*[A-Za-z_]+\\w*)"); 

該模式匹配:"#Name", " #Location", " #Rating"(和以前一樣)。
但分裂返回:["", "", "Name", "", " ", "Location"," New York", " ", "Rating",""]

問題2:這導致完全相同的匹配的圖案,結果在一個完全不同的分割輸出。這怎麼可能??

+1

首先,一般建議:在C#中使用逐字字符串正則表達式,以避免雙反斜線例如:'@ 「(\ s | ^)+#(\ w * [A-Za-z _] + \ w *)」' –

+0

thx,我不知道,我沒經常使用C# – rob

回答

2

原因是從MSDN這樣的句子:

如果捕獲括號在一個Regex.Split表達所使用的,任何捕獲文本被包括在生成的字符串數組英寸

你不應該在Split中使用捕獲組,如果你真的只想在匹配時拆分字符串。你能避免捕獲組,在每個地方你(...)有使用(?:...)

此外,爲你正確地估計。第一個和最後一個""的事實,串並開始用火柴(所以之前和之後,這些比賽將在拆分報空字符串)結束起源。

這裏是一個正則表達式,是更適合你的目的:

@"(?:^|\s+)#\w*[A-Za-z_]+\w*" 

注意,具有+以外的第一個子模式也是不必要的,並導致尷尬的副作用。首先,它允許組捕捉到多次(這就是爲什麼你有兩個此外"""":一個用於^,一個用於\s)。其次,沒有必要在第一個空格字符已經匹配後重復^,所以它足以只重複空格字符。另外,根本不需要在#之後對該單詞進行分組。

但是,如果你想要的是當它在字符串的開頭或前面有一個空格,以匹配類似#name(即不是*由**非空格字符前面),爲什麼包括可能在比賽中的所有空間。一個負面的後顧之門給你一個很好的出路:

@"(?<!\S)#\w*[A-Za-z_]+\w*" 

這正是如上所述。 (?<!\S)匹配如果沒有非空格字符(如果沒有包含空格字符的話)。這涵蓋了兩種情況沒有改變,你不需要Trim你的關鍵名稱。

+0

哇,這個解釋了很多:)關於正則表達式模式本身,我將它改爲'@「(?:^ | \ s +)#(?:\ w *)['。需要匹配#在字符串的開頭或字符串中的白色字符後面的約束條件是:允許'[A-Za-z],允許'_'(至少其中一個是強制的y),允許'\ d'。它似乎工作到目前爲止,但也許這可以實現更容易... – rob

+0

@rob然後你需要什麼'分裂'? –

+0

@m好吧,它的用戶輸入形式是#key1值vablue值#key2更多值#name_of_key等等,我需要首先匹配鍵值,然後獲得相應的值:) – rob

0

因爲您正在拆分的正則表達式匹配1個或多個空格,後跟散列('#'),後跟1個或多個單詞字符。

任何匹配的內容都不包含在結果中。

有兩種方法可以做到這一點:

  1. 拆分對什麼是不想和篩選結果。
  2. 積極尋找想要的東西。

下面是一些代碼與上述兩個選項:

static void Main(string[] args) 
{ 
    string sourceText = "#Name #Location New York #Rating" ; 

    // option 1: split on whitespace and then toss whatever isn't wanted 
    string[] hashTokens1 = sourceText.Split().Where(x => x.StartsWith("#")).ToArray() ; 

    // option 2: actively search for what is desired 
    string[] hashTokens2 = ParseSourceData(sourceText).ToArray() ; 

    return ; 

} 

private static readonly Regex hashTokenPattern = new Regex(@"#\w+"); 
private static IEnumerable<string> ParseSourceData(string s) 
{ 
    for (Match m = hashTokenPattern.Match(s) ; m.Success ; m = m.NextMatch()) 
    { 
     yield return m.Value ; 
    } 
} 

我自己,我會使用第二個選項,因爲它更好的狀態的情況下你要完成的任務。一個好的一般規則是喜歡肯定的斷言或測試而不是消極的。

你也可以寫第二個選項是「一行程序,即:

// option 2: actively search for what is desired 
Regex hashTokenPattern = new Regex(@"#\w+"); 
string[] hashTokens2 = hashTokenPattern.Matches(sourceText).Cast<Match>().Select(x=>x.Value).ToArray();