2011-09-21 25 views
0

我有一個文本編輯器,類似於堆棧溢出時使用的文本編輯器。我正在處理c#中的文本字符串,但也允許用戶使用自定義標籤來格式化文本。例如..c#替換自定義標記

<year /> will output the current year. 
"Hello <year /> World" would render Hello 2012 World 

我想要做的是創造一個正則表達式來搜索<year />任何一次出現字符串和替換它。除此之外,我還想給標籤添加屬性,並能夠提取它們,使其如此<year offset="2" format="5" />。我對RegEx不太瞭解,但希望有人知道如何做到這一點?

感謝

+0

是您的文件*實際* XML?這將使它更容易... –

+0

你需要逃避角色,你的標記沒有通過。 – Hammerstein

+0

這只是一個C#字符串。 – tmutton

回答

2

理想情況下,你不應該使用正則表達式;但看到Html敏捷包沒有HtmlReader我想你必須。

這就是說,看着其他標記解決方案,他們經常使用正則表達式模式和相關替換列表 - 所以我們不應該寫一個'一般'的情況下(例如<([A-Z][A-Z0-9]*)>.*?</\1>將是在這裏做錯了什麼,相反,我們想要<year>.*?</year>)。

起初你可能會創建一個類來保存與識別的標記信息,例如:

public class Token 
{ 
    private Dictionary<string, string> _attributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 
    public string InnerText { get; private set; } 

    public string this[string attributeName] 
    { 
     get 
     { 
      string val; 
      _attributes.TryGetValue(attributeName, out val); 
      return val; 
     } 
    } 

    public Token(string innerText, IEnumerable<KeyValuePair<string, string>> values) 
    { 
     InnerText = innerText; 
     foreach (var item in values) 
     { 
      _attributes.Add(item.Key, item.Value); 
     } 
    } 

    public int GetInteger(string name, int defaultValue) 
    { 
     string val; 
     int result; 
     if (_attributes.TryGetValue(name, out val) && int.TryParse(val, out result)) 
      return result; 
     return defaultValue; 
    } 
} 

現在,我們需要創建的正則表達式。例如,一個正則表達式匹配您的year元素看起來像:

<Year(?>\s*(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*>(?<itext>.*?)</Year> 

因此,我們可以推廣爲:

<{0}\s*(?>(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*>(?<itext>.*?)</{0}> 
<{0}\s*(?>(?<aname>\w*?)\s*=\s*"(?<aval>[^"]*)"\s*)*/> 

鑑於這些普通標籤的正則表達式,我們可以寫標記類:

public class MyMarkup 
{ 
    // These are used to build up the regex. 
    const string RegexInnerText = @"<{0}\s*(?>(?<aname>\w*?)\s*=\s*""(?<aval>[^""]*)""\s*)*>(?<itext>.*?)</{0}>"; 
    const string RegexNoInnerText = @"<{0}\s*(?>(?<aname>\w*?)\s*=\s*""(?<aval>[^""]*)""\s*)*/>"; 

    private static LinkedList<Tuple<Regex, MatchEvaluator>> _replacers = new LinkedList<Tuple<Regex, MatchEvaluator>>(); 

    static MyMarkup() 
    { 
     Register("year", false, tok => 
     { 
      var count = tok.GetInteger("digits", 4); 
      var yr = DateTime.Now.Year.ToString(); 
      if (yr.Length > count) 
       yr = yr.Substring(yr.Length - count); 
      return yr; 
     }); 
    } 

    private static void Register(string tagName, bool supportsInnerText, Func<Token, string> replacement) 
    { 
     var eval = CreateEvaluator(replacement); 

     // Add the no inner text variant. 
     _replacers.AddLast(Tuple.Create(CreateRegex(tagName, RegexNoInnerText), eval)); 
     // Add the inner text variant. 
     if (supportsInnerText) 
      _replacers.AddLast(Tuple.Create(CreateRegex(tagName, RegexInnerText), eval)); 
    } 

    private static Regex CreateRegex(string tagName, string format) 
    { 
     return new Regex(string.Format(format, Regex.Escape(tagName)), RegexOptions.Compiled | RegexOptions.IgnoreCase); 
    } 

    public static string Execute(string input) 
    { 
     foreach (var replacer in _replacers) 
      input = replacer.Item1.Replace(input, replacer.Item2); 
     return input; 
    } 

    private static MatchEvaluator CreateEvaluator(Func<Token, string> replacement) 
    { 
     return match => 
     { 
      // Grab the groups/values. 
      var aname = match.Groups["aname"]; 
      var aval = match.Groups["aval"]; 
      var itext = match.Groups["itext"].Value; 

      // Turn aname and aval into a KeyValuePair. 
      var attrs = Enumerable.Range(0, aname.Captures.Count) 
       .Select(i => new KeyValuePair<string, string>(aname.Captures[i].Value, aval.Captures[i].Value)); 

      return replacement(new Token(itext, attrs)); 
     }; 
    } 
} 

這些都是非常艱苦的工作,但它應該給你一個你應該做什麼的好主意。

0

string.Replace就足夠了第一個要求 - 無需使用正則表達式。

string.Replace(myString, "<year />", @"<year offset=""2"" /">") 

爲了提取屬性值 - 你可以split"

var val = @"<year offset=""2"" /">".Split('"')[1]; 

更新(以下評論):

您可以嘗試使用Html Agility Pack解析和操縱文字。它在HTML片段上運行良好 - 雖然我不確定它將如何處理自定義標籤(值得一試),但它的運行良好並且形式不正常。它可能雖然是矯枉過正。

+0

我不認爲他需要用另一個語法替換一個語法,但要處理它們(提取第二個語法中的參數,如果存在的話):) – Marco

+0

是的,我需要提取屬性值 – tmutton

+0

我需要添加多於一個屬性的標籤也。 – tmutton