2013-10-11 40 views
3

我正在研究一個基於網絡的幫助系統,該系統將自動插入解釋性文本中的鏈接,將用戶引導至其他幫助主題。我有上百個術語應該被鏈接,即RegEx for a Glossary Function

「手冊和標籤」(大致描述了這些概念) 「刪除手冊和標籤」(描述這一特定的行動) 「進一步瞭解如何加入說明書和標籤」 (再次,更具體的行動)

我有一個正則表達式來查找/替換整個單詞(好醇'\ b),這很好,除了在其他鏈接的術語內發現的鏈接的術語。相反的:

<a href="#">Learn more about manuals and labels</a> 

我結束了

<a href="#">Learn more about <a href="#">manuals and labels</a></a> 

這讓大家哭了一點點。更改其中的條款將被替換的順序(去最短至最長),意味着我將向獲得:

Learn more about <a href="#">manuals and labels</a> 

沒有外部鏈接我真正需要的。

更復雜的是,搜索字詞的大小可以變化,我需要保留原始大小寫。如果我可以做這樣的事情,我會準備就緒:

Regex _regex = new Regex("\\b" + termToFind + "(|s)" + "\\b", RegexOptions.IgnoreCase); 

string resultingText = _regex.Replace(textThatNeedsLinksInserted, "<a>" + "$&".Replace(" ", "_") + "</a>)); 

全部條款,完成此之後,去掉「_」,這將是完美的。 「Learn_more_about_manuals_and_labels」不符合「手冊和標籤」,一切都很好。

編寫文本時,很難讓幫助作者劃定需要替換的術語 - 他們不習慣編碼。而且,這會限制後面添加新詞的靈活性,因爲我們必須返回併爲所有先前編寫的文本添加分隔符。

是否有RegEx讓我用原始匹配中的「_」替換空格?還是有一個不同的解決方案,避開我?

+0

你能澄清一下你的意思是什麼「鏈接的術語」,並顯示「termToFind'和」textThatNeedsLinksInserted「代表什麼的完整示例?這將有助於看到前後的例子。 –

+0

termToFind將是「瞭解更多關於手冊和標籤」,「手冊和標籤」,「刪除手冊和標籤」。 –

+0

textThatNeedsLinksInserted可能是「無法使用手冊和標籤?瞭解有關添加手冊和標籤的更多信息,需要刪除它們?嘗試刪除手冊和標籤。」 –

回答

1

從嵌套鏈接你的例子,它聽起來就像你正在做單獨的內部串傳遞條款並執行多個Regex.Replace調用。由於你使用的是正則表達式,所以你應該讓它完成繁重的工作,並且把一個很好的模式放在一起,這樣就可以使用交替。

換句話說,你可能會想這樣的模式:\b(term1|term2|termN)\b

var input = "Having trouble with your manuals and labels? Learn more about adding manuals and labels. Need to get rid of them? Try to delete manuals and labels."; 
var terms = new[] 
{ 
    "Learn more about adding manuals and labels", 
    "Delete Manuals and Labels", 
    "manuals and labels" 
}; 

var pattern = @"\b(" + String.Join("|", terms) + @")\b"; 
var replacement = @"<a href=""#"">$1</a>"; 
var result = Regex.Replace(input, pattern, replacement, RegexOptions.IgnoreCase); 
Console.WriteLine(result); 

現在,以解決每個術語相應href的值的問題,您可以使用字典和改變正則表達式使用一個MatchEvaluator將返回自定義格式並查找字典中的值。字典也通過傳入StringComparer.OrdinalIgnoreCase來忽略案例。我在組的開頭部分加入了?:以使其成爲非捕獲組,因爲我不再像第一個示例中那樣引用捕獲的項目,所以稍微調整了模式。

var terms = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) 
{ 
    { "Learn more about adding manuals and labels", "2.html" }, 
    { "Delete Manuals and Labels", "3.html" }, 
    { "manuals and labels", "1.html" } 
}; 

var pattern = @"\b(?:" + String.Join("|", terms.Select(t => t.Key)) + @")\b"; 
var result = Regex.Replace(input, pattern, 
    m => String.Format(@"<a href=""{0}"">{1}</a>", terms[m.Value], m.Value), 
    RegexOptions.IgnoreCase); 

Console.WriteLine(result); 
+0

啊哈!我真的很喜歡那樣。唯一的問題是,從你的例子來看,周圍的文本將是相同的,不管匹配的是什麼術語。如果我希望href在第一學期爲1.html,第二學期爲2.html等,我可以做一個額外的捕獲組,每個學期都有目的地htmls(與術語本身的順序相同?) –

+0

@TimWestover我只是在努力!我更新了我的答案,以顯示如何在'Regex.Replace'調用中使用字典和MatchEvaluator來查找適當的值。 –

+0

這是完美的。奇蹟般有效。我調整它只是一點點,以支持一個簡單的複數(一個額外的),以及。 –

0

首先,您可以通過使用lookbehind來阻止您的正則表達式manuals and labels找到Learn more about manuals and labels。修改你的正則表達式如下:

(?<!Learn more about)(manuals and labels) 

但是對於您的具體要求,我會提出一個不同的解決方案。你應該爲你的正則表達式或兩者定義一個規則或優先級列表。一個可能的規則可能是「總是先搜索匹配大多數字符的正則表達式」。然而,這要求你的正則表達式總是固定長度。並且它不會阻止一個正則表達式消耗和替換將被不同的正則表達式匹配的字符(甚至可能是相同的大小)。

當然,你將需要添加額外的回顧後和預讀到每個regexs,以防止更換是您的替換元素

1

我會用一個有序的詞典是這樣,確保最小項是最後:

using System; 
using System.Text.RegularExpressions; 
using System.Collections.Specialized; 

public class Test 
{ 
    public static void Main() 
    { 
     OrderedDictionary Links = new OrderedDictionary(); 
     Links.Add("Learn more about adding manuals and labels", "2"); 
     Links.Add("Delete Manuals and Labels", "3"); 
     Links.Add("manuals and labels", "1"); 

     string text = "Having trouble with your manuals and labels? Learn more about adding manuals and labels. Need to get rid of them? Try to delete manuals and labels."; 

     foreach (string termToFind in Links.Keys) 
     { 
      Regex _regex = new Regex(@"\b" + termToFind + @"s?\b(?![^<>]*</)", RegexOptions.IgnoreCase); 
      text = _regex.Replace(text, @"<a href=""" + Links[termToFind] + @".html"">$&</a>"); 
     } 
     Console.WriteLine(text); 
    } 
} 

ideone demo

我加負先行((?![^<>]*</))防止部分的取代您之前已經更換過了錨標籤之間。