C # Именованные параметры в строке, которые заменяют значения параметров - PullRequest
7 голосов
/ 19 декабря 2008

Я хочу хорошим способом производительности (надеюсь) заменить именованный параметр в моей строке на именованный параметр из кода, например, моя строка:

"Hi {name}, do you like milk?"

Как я могу заменить {имя} кодом, регулярными выражениями? Чтобы дорого? Какой способ вы рекомендуете?

Как они в примере NHibernates HQL заменяют: my_param на пользовательское значение? Или в ASP.NET (MVC) Routing, который мне больше нравится, "{controller} / {action}", new {controller = "Hello", ...}?

Ответы [ 9 ]

20 голосов
/ 19 декабря 2008

Вы подтвердили, что регулярные выражения слишком дороги?

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

Тем не менее, вы не можете просто использовать самый простой способ, то есть Replace?

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));
13 голосов
/ 19 декабря 2008

Regex, безусловно, жизнеспособный вариант, особенно с MatchEvaluator:

    Regex re = new Regex(@"\{(\w*?)\}", RegexOptions.Compiled); // store this...

    string input = "Hi {name}, do you like {food}?";

    Dictionary<string, string> vals = new Dictionary<string, string>();
    vals.Add("name", "Fred");
    vals.Add("food", "milk");

    string q = re.Replace(input, delegate(Match match)
    {
        string key = match.Groups[1].Value;
        return vals[key];
    });
3 голосов
/ 19 декабря 2008

Теперь, если у вас есть замены в словаре, например:

    var  replacements = new Dictionary<string, string>();
    replacements["name"] = "Mike";
    replacements["age"]= "20";

тогда регулярное выражение становится довольно простым:

Regex regex = new Regex(@"\{(?<key>\w+)\}");
    string formattext = "{name} is {age} years old";
    string newStr = regex.Replace(formattext, 
            match=>replacements[match.Groups[1].Captures[0].Value]);
2 голосов
/ 31 марта 2011

Подумав об этом, я понял, что на самом деле хотел бы, чтобы String.Format () принимала IDictionary в качестве аргумента и чтобы шаблоны могли быть написаны с использованием имен вместо индексов.

Для подстановок строк с большим количеством возможных ключей / значений индексные номера приводят к неразборчивым шаблонам строк - и в некоторых случаях вы можете даже не знать, какие элементы будут иметь какое число, поэтому я придумал следующее расширение :

https://gist.github.com/896724

По сути, это позволяет вам использовать строковые шаблоны с именами вместо чисел и словарь вместо массива, а также иметь все другие полезные функции String.Format (), позволяющие при необходимости использовать пользовательский IFormatProvider. и позволяя использовать весь обычный синтаксис форматирования - точность, длина и т. д.

Пример , предоставленный в справочном материале для String.Format, является отличным примером того, как шаблоны со многими пронумерованными элементами становятся полностью неразборчивыми - портируя этот пример для использования этого нового метода расширения, вы получаете что-то вроде этого :

var replacements = new Dictionary<String, object>()
                       {
                           { "date1", new DateTime(2009, 7, 1) },
                           { "hiTime", new TimeSpan(14, 17, 32) },
                           { "hiTemp", 62.1m },
                           { "loTime", new TimeSpan(3, 16, 10) },
                           { "loTemp", 54.8m }
                       };

var template =
    "Temperature on {date1:d}:\n{hiTime,11}: {hiTemp} degrees (hi)\n{loTime,11}: {loTemp} degrees (lo)";

var result = template.Subtitute(replacements);

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

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

...

Для удобства я попытался добавить метод, который принимал бы анонимный объект вместо словаря:

public static String Substitute(this String template, object obj)
{
    return Substitute(
        template,
        obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null))
    );
}

По какой-то причине это не работает - передача анонимного объекта, такого как new { name: "value" } этому методу расширения, выдает сообщение об ошибке во время компиляции, говорящее, что лучшим соответствием была версия IDictionary этого метода. Не уверен, как это исправить. (Кто?)

1 голос
/ 03 февраля 2010

Как насчет

stringVar = "Hello, {0}. How are you doing?";
arg1 = "John";    // or args[0]
String.Format(stringVar, arg1)

Вы можете даже иметь несколько аргументов, просто увеличьте {x} и добавьте еще один параметр в метод Format (). Не уверен, что разные, но оба "string" и "String" имеют этот метод.

0 голосов
/ 09 ноября 2011

Я бы пошел на решение mindplay.dk ... Работает довольно хорошо.

И, с небольшими изменениями, он поддерживает шаблоны-шаблоны, такие как «Привет {имя}, тебе нравится {0}?», Заменив {имя}, но сохранив {0}:

В данном источнике (https://gist.github.com/896724), заменить следующим образом:

        var format = Pattern.Replace(
            template,
            match =>
                {
                    var name = match.Groups[1].Captures[0].Value;

                    if (!int.TryParse(name, out parsedInt))
                    {
                        if (!map.ContainsKey(name))
                        {
                            map[name] = map.Count;
                            list.Add(dictionary.ContainsKey(name) ? dictionary[name] : null);
                        }

                        return "{" + map[name] + match.Groups[2].Captures[0].Value + "}";
                    }
                    else return "{{" + name + "}}";
                }
            );

Кроме того, он поддерживает длину ({name, 30}), а также спецификатор форматов или их комбинацию.

0 голосов
/ 31 октября 2011

или попробуйте это с Linq, если у вас есть все значения замены, хранящиеся в словаре obj.

Например:

Dictionary<string,string> dict = new Dictionary<string,string>();
dict.add("replace1","newVal1");
dict.add("replace2","newVal2");
dict.add("replace3","newVal3");

var newstr = dict.Aggregate(str, (current, value) => current.Replace(value.Key, value.Value));

dict - это ваш объект словаря, заданный для поиска и замены. str - это ваша строка, с которой вам нужно сделать некоторые замены.

0 голосов
/ 29 марта 2009

Попробуйте использовать StringTemplate. Это намного мощнее, но делает работу безупречной.

0 голосов
/ 19 декабря 2008

Скомпилированное регулярное выражение может добиться цели, особенно если нужно заменить много токенов. Если их всего несколько, а производительность - это ключ, я просто нашел бы токен по индексу и заменил бы его строковыми функциями. Верьте или нет, это будет быстрее, чем регулярное выражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...