2013-05-15 2 views
3

Предположим, я хочу спросить у пользователя, в каком формате они хотят, чтобы определенный выход был включен, и выход будет включать поля заполнения. Таким образом, они обеспечивают что-то вроде этой строки:Лучший способ предоставить пользователю escape-строку

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

Все, что связанное с помощью {} должно быть именем столбца в таблице где-то они будут заменены на сохраненное значение с кодом, которую я пишу. Кажется простым, я мог бы просто сделать строку. Замените на любой экземпляр, соответствующий совпадению "{" + FieldName + "}". Но, что, если я также хочу дать пользователю возможность использовать escape, чтобы они могли использовать скобки, как любая другая строка. Я думал, что они предоставляют «{{" или "}}", чтобы избежать этой скобки - приятно и легко для них. Таким образом, они могли бы обеспечить что-то вроде:

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

Но теперь, «{{FieldName2Value}}» должен рассматриваться как любой другой строки и игнорировали путем Заменить. Кроме того, если они решили поставить что-то вроде «{{{FieldName2Value}}}» с тройными скобками, это будет интерпретироваться кодом как значение поля, заключенное в скобки и т. Д.

Здесь я застреваю. Я пытаюсь с RegEx и придумал это:

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

NICE! Вы являетесь мастером регулярных выражений. Работать понравилось очарование. Даже если вы используете что-то вроде {{{{FieldName}}}}, он корректно возвращает {{FieldName}}. Спасибо за помощь! – Ernie

0

RegEx не будет делать то, что вы хотите, потому что он знает, что это текущее состояние и какие переходы доступны. У него нет понятия памяти. Язык, который вы пытаетесь разобрать, не является регулярным, поэтому вы никогда не сможете написать RegEx для обработки общего случая. Вам понадобятся выражения i, где i - количество подходящих фигурных скобок.

Существует много теории, и я дам некоторые ссылки внизу, если вам интересно. Но в основном язык, который вы пытаетесь проанализировать, не имеет контекста и для реализации общего решения вам понадобится модель push-down automaton, которая использует стек, чтобы гарантировать, что открывающая скобка имеет подходящую закрывающую скобу (да, это почему большинство языков имеют соответствующие фигурные скобки).

Каждый раз, когда вы сталкиваетесь с {, вы кладете его в стек. Если вы столкнулись с }, вы выскочите из стека. Когда вы очистите стек, вы поймете, что достигли конца поля. Конечно, это серьезное облегчение проблемы, но если вы ищете общее решение, оно должно заставить вас двигаться в правильном направлении.

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}}} становится {значением}, что означает {{{{FieldName}}}}? Что относительно {{{{{FieldName}}}}} и так далее? Хотя эти случаи маловероятны, их все же необходимо обрабатывать целенаправленно.

+0

Спасибо, что поделились кодом Ben. Да, последний абзац поражает гвоздь на голове - извините, я должен был сформулировать это лучше в моем оригинальном посте. Код drf очень хорошо переносит этот код. – Ernie

Смежные вопросы