2013-10-29 21 views
3

對於新問題的道歉,但C#不是我的第一語言。如何正確匹配C#中的字詞分隔符而不匹配其他字符

我試圖建立一個索引列表中的所有分隔符之間的單詞,在給定的內容,佔標點符號。我希望能夠使用正則表達式\ b(單詞'邊界'),但它匹配我沒有預料到的各種東西。下面是我寫的方法:

internal static IList<int> GetBreakIndexesInContent(string content) 
{ 
    IList<int> indices = new List<int>(); 
    if (content != null) 
    { 
     foreach (Match match in Regex.Matches(content, @"\b")) 
     { 
      Console.WriteLine("INDEX:[" + match.Index + "] CHAR:[" + content.Text[match.Index] + "] UNICODE:[" + (int)content.Text[match.Index] + "]"); 
      indices.Add(match.Index); 
     } 
    } 
    return indices; 
} 

考慮下面的100個字符的字符串:

"Lorem ipsum dolor sit amet, tritani quaestio suscipiantur mea ea, duo et impedit facilisi evertitur." 

我期待我的方法來產生一個列表的長度,其中第一個指標是14種元素5,第二個位置11,依此類推(忽略26和64位的逗號,以及99的時間段)。相反,這是我得到的輸出:

//COUNT: [30] 
INDEX:[0] CHAR:[L] UNICODE:[76] 
INDEX:[5] CHAR:[ ] UNICODE:[32] 
INDEX:[6] CHAR:[i] UNICODE:[105] 
INDEX:[11] CHAR:[ ] UNICODE:[32] 
INDEX:[12] CHAR:[d] UNICODE:[100] 
INDEX:[17] CHAR:[ ] UNICODE:[32] 
INDEX:[18] CHAR:[s] UNICODE:[115] 
INDEX:[21] CHAR:[ ] UNICODE:[32] 
INDEX:[22] CHAR:[a] UNICODE:[97] 
INDEX:[26] CHAR:[,] UNICODE:[44] 
INDEX:[28] CHAR:[t] UNICODE:[116] 
INDEX:[35] CHAR:[ ] UNICODE:[32] 
INDEX:[36] CHAR:[q] UNICODE:[113] 
INDEX:[44] CHAR:[ ] UNICODE:[32] 
INDEX:[45] CHAR:[s] UNICODE:[115] 
INDEX:[57] CHAR:[ ] UNICODE:[32] 
INDEX:[58] CHAR:[m] UNICODE:[109] 
INDEX:[61] CHAR:[ ] UNICODE:[32] 
INDEX:[62] CHAR:[e] UNICODE:[101] 
INDEX:[64] CHAR:[,] UNICODE:[44] 
INDEX:[66] CHAR:[d] UNICODE:[100] 
INDEX:[69] CHAR:[ ] UNICODE:[32] 
INDEX:[70] CHAR:[e] UNICODE:[101] 
INDEX:[72] CHAR:[ ] UNICODE:[32] 
INDEX:[73] CHAR:[i] UNICODE:[105] 
INDEX:[80] CHAR:[ ] UNICODE:[32] 
INDEX:[81] CHAR:[f] UNICODE:[102] 
INDEX:[89] CHAR:[ ] UNICODE:[32] 
INDEX:[90] CHAR:[e] UNICODE:[101] 
INDEX:[99] CHAR:[.] UNICODE:[46] 

的原因,我不能簡單地試圖匹配" "或更高版本只是針對過濾ASCII 32,是因爲這需要是不外語敏感必須在所有單詞之間使用空格。另外,因爲我不想無意中將多個空間捕捉爲單獨的「分隔符」。

我真的希望\b將是一個很好的標準抓住所有真正的詞分離,但它似乎並非如此。我可以「自己動手」,但是如果C#已經具備某種設備來處理這個問題,那麼我希望自己能夠省去重新發明輪子的麻煩。

當然,任何幫助,將不勝感激。

謝謝, Greg。

+2

'\ b'被稱爲_zero-width_轉義序列---它在技術上實際上不匹配任何_character_ ---但匹配所有,呃... _boundaries_,這就是爲什麼你看到更多結果超出您的預期。試試'\ b(?<= [a-zA-Z])'。這使用了一種叫做_positive lookbehind assertion_的東西來聲明邊界之前的字符是一個字母。如果你不希望_spaces_成爲這些邊界的一部分,那麼嘗試使用'\ b(?<= [a-zA-Z])(?!\ s)',它僅僅添加一個_negative lookahead assertion_來斷言那邊界之後的角色不是空格。 –

+1

閱讀這篇文章的更多信息:http://www.regular-expressions.info/wordboundaries.html –

回答

1

我不是故意要鍵入這麼長的評論。我想我不妨將它移到答案上。

\b匹配詞和非單詞字符之間的所有邊界,\w\W,包括字符串的開頭和你的第一個字母之間,字母和空格之間(在空間的兩側),和等等。

您可能需要將您的表情與環視斷言結合起來才能實現您想要的效果。

例如,

\b(?<=[a-zA-Z]) 

使用積極向後斷言,以確保您只匹配遵循字母單詞邊界。然而,這會考慮空間的分隔符,這我不知道你想做的事,在這種情況下,

\b(?<=[a-zA-Z])(?!\s) 

增加了一個附加條件—這個時候式斷言,以確保您只匹配單詞邊界之後沒有空格字符。

+0

謝謝!看來,我對正則表達式也比較無知。我從來沒有聽說過周圍的東西。現在閱讀他們!你的回答讓我想到了我需要的地方。再看一下lookarounds,我確信我可以休息。 :) 再次感謝! –

+0

@GregGauthier - 很高興幫助。如果您遇到困難,請隨時評論回來。 Lookarounds可能是一件棘手的事情。爲了防止有一天您可能會將正則表達式帶到其他語言中,請記住,C#具有特別靈活的lookarounds實現,例如,大多數其他語言在C#中不支持「可變寬度lookbehinds」而一些像Javascript這樣的語言根本不支持lookbehinds。 –

1

字boundry比賽地點例如:

In Lorem ipsum dolor sit amet, 
^^^ ^^ ^^ ^^ ^^ ^^ 

所以你可以看到它的許多更多的比賽比你想象的。

從技術上講,邊界是一個斷言。斷言在「字符之間」存在。
當他們坐在角色之間時,他們傾向於向前看或向後看。

所以\b可能是要麼(?<=\w)(?=\W|$)(?<=\W|^)(?=\w)

2

如果正則表達式中的單詞字符的定義(\w)滿足您的需要(可以對其進行閱讀),您可以匹配非單詞字符(例如,通過使用其反字符類別, \W。該解決方案可能是簡單的

private static readonly Regex rxWord = new Regex(@"\w+") ; 
static IEnumerable<string> ParseWords(string s) 
{ 
    return rxWord.Matches(s).Cast<Match>().Select(m => m.Value) ; 
} 

private static Regex rxNonWord = new Regex(@"\W+") ; 
private static IEnumerable<string> ParseNonWords(string s) 
{ 
    return rxNonWord.Matches(s).Cast<Match>().Select(m => m.Value) ; 
} 

但是,從你說的話,你現在要做的,它可能是更容易從Unicode categories that the CLR supports

而且撰寫你的性格類或單詞分隔,使用正則表達式「單詞」和「非單詞」類(\w\W)以及它們之間的邊界(\b)可能不起作用,因爲在正則表達式中,一個「單詞」不一定就是您認爲的那樣。字符類別\w起始於C語言標識符允許的字符集([A-Za-z0-9_])。如果你是一個使用正則表達式來通過符號源代碼進行grep的C程序員,這非常有用。對於通過文字來翻譯文字不太好。

\w在CLR正則表達式當前的定義是,它包含在任何這些Unicode類別的任何字符匹配:

  • ((字母,小寫)
  • 信,大寫)
  • LT(字母,標題情況)
  • (字母,其他)
  • Lm的(字母,改性劑)
  • (數字,十進制數)
  • PC(標點,連接器)此類別包括10個字符。在這裏最常遇到的一個,至少英文是_(0x005F),又名下劃線或LOWLINE。

所有這些都說\w是懶惰的寫作方式[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Lm}\p{Nd}\p{Pc}]

非字字符類\W與此相反。這是說[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Lm}\p{Nd}\p{Pc}]確切的等值。

零寬度錨\b不「匹配」任何東西:像它的姐妹^$\b錨比賽到一個特定的地方。在\b的情況下,該位置是字(\w)和非字(\W)字符之間的邊界。 \b有一個與其相反的表親\B:它將匹配定位在兩個單詞(\w)或兩個非單詞(\W)字符之間的邊界處。

所以......

您需要先拿出一個適合你的問題域「字」的定義。這比看起來更難:例如,「二十三」一個或兩個單詞? 「前妻」怎麼樣?或者像「抽象表現主義」這樣的複合詞怎麼樣,取決於上下文的東西是一個還是兩個詞(你會發現「抽象」,「表現主義」和「抽象表現主義」作爲詞典中的單個詞條)。

如果您可以定義符合該定義的字符類,一切都很好。要匹配您的單詞之間的插頁式內容,您只需定義其反向字符類即可。

如果一個簡單的字符類不會做你,你需要使用各種預見/後顧斷言來匹配你想要的。

+0

很棒的概述。儘管我沒有把它作爲「答案」,但我給它一個顛簸,因爲它確實值得點。 –