8

我希望以一种良好的性能方式(我希望)将我的字符串中的命名参数替换为代码中的命名参数,例如,我的字符串:

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

我怎么能用代码替换 {name} ,正则表达式?贵吗?你推荐哪种方式?

他们如何在示例 NHibernates HQL 中将 :my_param 替换为用户定义的值?或者在我更喜欢的 ASP.NET (MVC) 路由中,“{controller}/{action}”,new { controller = “Hello”,...}?

4

9 回答 9

20

你确认正则表达式太贵了吗?

正则表达式的成本被大大夸大了。对于这样一个简单的模式,性能将非常好,实际上可能只比直接搜索和替换稍差。另外,你Compiled在构造正则表达式的时候有没有尝试过flag?

也就是说,你不能只使用最简单的方法,即Replace

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));
于 2008-12-18T21:25:12.870 回答
14

正则表达式当然是一个可行的选择,尤其是使用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];
    });
于 2008-12-18T21:26:50.073 回答
4

现在,如果您在字典中有替换项,如下所示:

    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]);
于 2008-12-18T21:45:19.863 回答
2

考虑到这一点,我意识到我真正想要的是 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 版本。不知道如何解决。(任何人?)

于 2011-03-31T17:06:07.767 回答
1

怎么样

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

你甚至可以有多个参数,只需增加 {x} 并将另一个参数添加到 Format() 方法。不确定不同,但“字符串”和“字符串”都有这种方法。

于 2010-02-03T17:41:05.957 回答
0

编译的正则表达式可能会解决问题,尤其是在要替换许多标记的情况下。如果只有少数几个并且性能是关键,我会简单地通过索引找到令牌并使用字符串函数替换。信不信由你,这将比正则表达式更快。

于 2008-12-18T21:26:06.517 回答
0

尝试使用字符串模板。它比这更强大,但它完美地完成了这项工作。

于 2009-03-29T01:15:14.683 回答
0

或者如果您将所有替换值都存储在 Dictionary obj 中,请尝试使用 Linq。

例如:

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是您的搜索替换对定义的 Dictionary 对象。 str是您需要进行一些替换的字符串。

于 2011-10-31T18:12:35.513 回答
0

我会选择 mindplay.dk 解决方案......效果很好。

并且,稍作修改,它支持模板模板,例如“Hi {name},你喜欢 {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}) 以及格式说明符,或两者的组合。

于 2011-11-09T16:05:44.853 回答