C# именованные параметры строки, которые заменяют значения параметров
Я хочу в хорошей производительности (я надеюсь) заменить именованный параметр в моей строке на именованный параметр из кода, например, моя строка:
"Hi {name}, do you like milk?"
Как я могу заменить {name} кодом, регулярными выражениями? Дорого? Какой путь вы рекомендуете?
как они в Примере NHibernates HQL заменяют: my_param на пользовательское значение? Или в ASP.NET (MVC) маршрутизация, которая мне больше нравится, "{controller}/{action}", новый { controller = "Hello",... }?
9 ответов
вы подтвердили, что регулярные выражения слишком дороги?
Стоимость регулярных выражений сильно преувеличена. Для такого простого шаблона производительность будет довольно хорошей, возможно, только немного менее хорошей, чем прямой поиск и замена. Кроме того, вы экспериментировали с Compiled
флаг при построении регулярного выражения?
тем не менее, вы не можете просто использовать самый простой способ, то есть Replace
?
string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));
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];
});
теперь, если у вас есть замены в словаре, например:
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]);
подумав об этом, я понял, что на самом деле я хотел, это была эта строка.Format () будет принимать IDictionary в качестве аргумента, и что шаблоны могут быть написаны с использованием имен вместо индексов.
для подстановок строк с большим количеством возможных ключей / значений индексные номера приводят к неразборчивым шаблонам строк - и в некоторых случаях вы можете даже не знать, какие элементы будут иметь какое число, поэтому я придумал следующее расширение:
https://gist.github.com/896724
В основном это позволяет использовать шаблоны строк с именами вместо чисел и словарь вместо массива и позволяет вам иметь все другие хорошие функции String.Format (), позволяющий использовать пользовательский IFormatProvider, если это необходимо, и позволяющий использовать весь обычный синтаксис форматирования - точность, длина и т. д.
на пример предусмотрено в справочном материале для Строка.Формат-отличный пример того, как шаблоны со многими нумерованными элементами становятся полностью неразборчивыми - перенося этот пример для использования этого нового метода расширения, вы получаете что-то вроде этого:
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 этого метода. Не знаю, как это исправить. (кто?)
как о
stringVar = "Hello, {0}. How are you doing?";
arg1 = "John"; // or args[0]
String.Format(stringVar, arg1)
У вас даже может быть несколько args, просто увеличьте {x} и добавьте другой параметр в метод Format (). Не уверен, что разные, но оба "string" и "String" имеют этот метод.
скомпилированное регулярное выражение может сделать трюк, особенно если есть много токенов для замены. Если их всего несколько, и производительность является ключевой, я бы просто нашел токен по индексу и заменил его строковыми функциями. Верить или нет это будет быстрее, чем регулярное выражение.
или попробуйте это с Linq, если у вас есть все ваши значения replace, хранящиеся в 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));
дикт ваш поиск-заменить пары, определенные объект словаря. str ваша строка, с которой вам нужно сделать некоторые замены.
Я бы пошел на игру ума.dk решение... Работать достаточно хорошо.
и, с небольшой модификацией, он поддерживает шаблоны-это шаблоны, как "Привет {имя}, вам нравится {0}?", заменив {name}, но сохранив {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}), а также formatspecifier или комбинацию обоих.