2013-05-15 26 views
3

假設我想問用戶他們想要某個輸出是什麼格式,輸出將包含填充字段。因此,他們提供這樣的字符串:爲用戶提供轉義字符串的最佳方法

"Output text including some field {FieldName1Value} and another {FieldName2Value} and so on..." 

凡是由{}應該是一個列名的地方,他們將與我寫的代碼存儲的值來替換表的約束。看起來很簡單,我可以做一個字符串。替換任何匹配模式「{」+ FieldName +「}」的實例。但是,如果我還想給用戶使用轉義的選項,那麼他們可以像使用其他任何字符串一樣使用括號。我認爲他們提供了「{{」或「}}」來避開那個括號 - 對他們來說很好,很容易。因此,他們可以提供類似:

"Output text including some field {FieldName1Value} and another {FieldName2Value} but not this {{FieldName2Value}}" 

但現在,「{{FieldName2Value}}」是被替換像任何其他字符串處理,忽略由。另外,如果他們決定在三個括號中加上「{{FieldName2Value}}}」,那麼代碼將會將其解釋爲用括號括起來的字段值,等等。

這是我卡住的地方。我用正則表達式嘗試,想出了這個:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
{ 
    string format = (string)values[0]; 
    ObservableCollection<CalloutFieldAliasMap> oc = (ObservableCollection<CalloutFieldAliasMap>)values[1]; 

    foreach (CalloutFieldMap map in oc) 
     format = Regex.Replace(format, @"(?<!{){" + map.FieldName + "(?<!})}", " " + map.FieldAlias + " ", RegexOptions.IgnoreCase); 

    return format; 
} 

這工作在雙括號的情況{{}}但如果有三個,即{{{}}}。當它應該被視爲{FieldValue}時,三個括號被視爲字符串。

感謝您的任何幫助。

回答

3

通過擴展您的正則表達式,可以容納文字的存在。

format = Regex.Replace(format, 
     @"(?<!([^{]|^){(?:{{)*){" + Regex.Escape(map.FieldName) + "}", 
     String.Format(" {0} ", map.FieldAlias), 
     RegexOptions.IgnoreCase | RegexOptions.Compiled); 

表達,(?<!([^{]|^){(?:{{)*){的第一部分,指定該{必須由偶數個{字符前面對於它來標記的場的令牌的開始。因此,{FieldName}{{{FieldName}將表示字段名稱的開始,而{{FieldName}{{{{FieldName}不會。

關閉}只需要該字段的末尾是一個簡單的}。語法上存在一些含糊不清的地方,{FieldName1Value}}}可能被解析爲與FieldName1Value(後面是文字})或FieldName1Value}的令牌。正則表達式假定前者。 。(如果是後者的目的,你可以用}(?!}(}})*)而不是替換此

一對夫婦的其他注意事項我加Regex.Escape(map.FieldName)所以,在字段名稱中的所有字符都被視爲文字;並增加了RegexOptions.Compiled標誌(因爲這個。是一個複雜的表達和在一個循環中執行的,它是爲編譯的良好候選者)

循環執行之後,一個簡單的:

format = format.Replace("{{", "{").Replace("}}", "}") 

可以用來取消轉義字面{{}}字符。

+0

好!你是正則表達式的主人。工作喜歡魅力。即使使用{{{FieldName}}}},它也會正確返回{{FieldName}}。謝謝您的幫助! – Ernie

0

RegEx不會做你想做的事情,因爲它只知道它的當前狀態和可用的轉換。它沒有記憶的概念。您正在嘗試解析的語言不是常規的,因此您將永遠無法編寫正則表達式來處理一般情況。您需要i表達式,其中i是匹配大括號的數目。

背後有很多理論,如果您好奇,我會在底部提供一些鏈接。但基本上你想要解析的語言是上下文無關的,爲了實現一個通用的解決方案,你需要建模一個下推自動機,它使用一個堆棧來確保一個左大括號有一個匹配的大括號(是的,這是爲什麼大多數語言都有大括號)。

每次遇到{時,都把它放在堆棧上。如果您遇到},您從堆棧彈出。當你清空堆棧時,你會知道你已經到達了一個字段的末尾。當然,這是對問題的一種主要簡化,但是如果您正在尋找一個通用的解決方案,它應該讓您朝着正確的方向前進。

http://en.wikipedia.org/wiki/Regular_language

http://en.wikipedia.org/wiki/Context-free_language

http://en.wikipedia.org/wiki/Pushdown_automaton

1

最簡單的方法是使用String.Replace與用戶不能(或幾乎肯定不會)輸入一個字符序列來代替雙括號。然後做替換你的領域,並最終轉換替換回雙括號。

例如,給定:

string replaceOpen = "{x"; // 'x' should be something like \u00ff, for example 
string replaceClose = "x}"; 

string template = "Replace {ThisField} but not {{ThatField}}"; 

string temp = template.Replace("{{", replaceOpen).Replace("}}", replaceClose); 
string converted = temp.Replace("{ThisField}", "Foo"); 

string final = converted.Replace(replaceOpen, "{{").Replace(replaceClose, "}}); 

這不是特別漂亮,但它是有效的。

你如何去做它將在很大程度上取決於你多久打一次電話,以及你真正需要多快。

+0

謝謝吉姆。這將是我的最後手段,但想看看是否有更多,我想你可以說,「優雅」的方式。 – Ernie

1

我有一個我寫的擴展方法,幾乎​​可以做你要求的,但是,雖然它使用雙大括號進行轉義,但它並不像你所建議的那樣做三大括號。這裏是方法(也GitHub上的https://github.com/benallred/Icing/blob/master/Icing/Icing.Core/StringExtensions.cs):

private const string FormatTokenGroupName = "token"; 
private static readonly Regex FormatRegex = new Regex(@"(?<!\{)\{(?<" + FormatTokenGroupName + @">\w+)\}(?!\})", RegexOptions.Compiled); 
public static string Format(this string source, IDictionary<string, string> replacements) 
{ 
    if (string.IsNullOrWhiteSpace(source) || replacements == null) 
    { 
     return source; 
    } 

    string replaced = replacements.Aggregate(source, 
     (current, pair) => 
      FormatRegex.Replace(current, 
       new MatchEvaluator(match => 
        (match.Groups[FormatTokenGroupName].Value == pair.Key 
         ? pair.Value : match.Value)))); 

    return replaced.Replace("{{", "{").Replace("}}", "}"); 
} 

用法:

"This is my {FieldName}".Format(new Dictionary<string, string>() { { "FieldName", "value" } }); 

即使你添加這個簡單:

public static string Format(this string source, object replacements) 
{ 
    if (string.IsNullOrWhiteSpace(source) || replacements == null) 
    { 
     return source; 
    } 

    IDictionary<string, string> replacementsDictionary = new Dictionary<string, string>(); 

    foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(replacements)) 
    { 
     string token = propertyDescriptor.Name; 
     object value = propertyDescriptor.GetValue(replacements); 

     replacementsDictionary.Add(token, (value != null ? value.ToString() : String.Empty)); 
    } 

    return Format(source, replacementsDictionary); 
} 

用法:

"This is my {FieldName}".Format(new { FieldName = "value" }); 

這種方法的單元測試在https://github.com/benallred/Icing/blob/master/Icing/Icing.Tests/Core/TestOf_StringExtensions.cs

如果這不起作用,您的理想解決方案會對三個以上的大括號做什麼?換句話說,如果{{{FieldName}}}變成{value},{{{FieldName}}}}會變成什麼樣子?那麼{{{{{FieldName}}}}}等等呢?雖然這些案件不太可能,但仍需要有目的地處理。

+0

感謝您分享代碼Ben。是的,你的最後一段碰到了頭上的釘子 - 對不起,我應該在原來的帖子中說得更清楚。 drf的代碼非常好。 – Ernie

相關問題