C# 将命名参数替换为参数值的字符串

发布于 2024-07-11 08:05:22 字数 300 浏览 11 评论 0原文

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

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

如何用代码、正则表达式替换 {name}? 贵吗? 您推荐哪种方式?

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

I want in a good performance way (I hope) replace a named parameter in my string to a named parameter from code, example, my string:

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

How could I replace the {name} by code, Regular expressions? To expensive? Which way do you recommend?

How do they in example NHibernates HQL to replace :my_param to the user defined value? Or in ASP.NET (MVC) Routing that I like better, "{controller}/{action}", new { controller = "Hello", ... }?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

萌无敌 2024-07-18 08:05:22

您是否确认正则表达式太昂贵?

正则表达式的成本被大大夸大了。 对于这样一个简单的模式,性能将非常好,事实上,可能只比直接搜索和替换稍差一些。 另外,您在构造正则表达式时是否尝试过使用 Compiled 标志?

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

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));

Have you confirmed that regular expressions are too expensive?

The cost of regular expressions is greatly exaggerated. For such a simple pattern performance will be quite good, probably only slightly less good than direct search-and-replace, in fact. Also, have you experimented with the Compiled flag when constructing the regular expression?

That said, can't you just use the simplest way, i.e. Replace?

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));
罪歌 2024-07-18 08:05:22

正则表达式当然是一个可行的选择,尤其是使用 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];
    });

Regex is certainly a viable option, especially with a 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];
    });
心是晴朗的。 2024-07-18 08:05:22

现在,如果您在字典中进行了替换,如下所示:

    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]);

Now if you have you replacements in a dictionary, like this:

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

then the Regex becomes quite simple:

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]);
友欢 2024-07-18 08:05:22

经过思考之后,我意识到我真正希望的是 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 版本。 不知道如何解决这个问题。 (任何人?)

After thinking about this, I realized what I actually wished for, was that String.Format() would take an IDictionary as argument, and that templates could be written using names instead of indexes.

For string substitutions with lots of possible keys/values, the index numbers result in illegible string templates - and in some cases, you may not even know which items are going to have what number, so I came up with the following extension:

https://gist.github.com/896724

Basically this lets you use string templates with names instead of numbers, and a dictionary instead of an array, and lets you have all the other good features of String.Format(), allowing the use of a custom IFormatProvider, if needed, and allowing the use of all the usual formatting syntax - precision, length, etc.

The example provided in the reference material for String.Format is a great example of how templates with many numbered items become completely illegible - porting that example to use this new extension method, you get something like this:

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);

As someone pointed out, if what you're writing needs to be highly optimized, don't use something like this - if you have to format millions of strings this way, in a loop, the memory and performance overhead could be significant.

On the other hand, if you're concerned about writing legible, maintainable code - and if you're doing, say, a bunch of database operations, in the grand scheme of things, this function will not add any significant overhead.

...

For convenience, I did attempt to add a method that would accept an anonymous object instead of a dictionary:

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

For some reason, this doesn't work - passing an anonymous object like new { name: "value" } to that extension method gives a compile-time error message saying the best match was the IDictionary version of that method. Not sure how to fix that. (anyone?)

爱的十字路口 2024-07-18 08:05:22

怎么样?

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

您甚至可以有多个参数,只需增加 {x} 并向 Format() 方法添加另一个参数即可。 不确定不同,但“string”和“String”都有这个方法。

How about

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

You can even have multiple args, just increment the {x} and add another parameter to the Format() method. Not sure the different but both "string" and "String" have this method.

童话 2024-07-18 08:05:22

编译后的正则表达式可能会达到目的,特别是当有许多标记需要替换时。 如果它们只有少数并且性能是关键,我会简单地通过索引找到标记并使用字符串函数进行替换。 不管你相信与否,这会比正则表达式更快。

A compiled regex might do the trick , especially if there are many tokens to be replaced. If there are just a handful of them and performance is key, I would simply find the token by index and replace using string functions. Believe it or not this will be faster than a regex.

定格我的天空 2024-07-18 08:05:22

尝试使用 StringTemplate。 它比这更强大,但它的工作却完美无缺。

Try using StringTemplate. It's much more powerful than that, but it does the job flawless.

穿透光 2024-07-18 08:05:22

或者如果您将所有替换值都存储在 Dictionary 对象中,请尝试使用 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 是您需要进行一些替换的字符串。

or try this with Linq if you have all your replace values stored in a Dictionary obj.

For example:

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 is your search-replace pairs defined Dictionary object.
str is your string which you need to do some replacements with.

凹づ凸ル 2024-07-18 08:05:22

我会选择 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})以及格式说明符,或两者的组合。

I would go for the mindplay.dk solution... Works quite well.

And, with a slight modification, it supports templates-of-templates, like
"Hi {name}, do you like {0}?", replacing {name} but retaining {0}:

In the given source (https://gist.github.com/896724), replace as follows:

        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 + "}}";
                }
            );

Furthermore, it supports a length ({name,30}) as well as a formatspecifier, or a combination of both.

肩上的翅膀 2024-07-18 08:05:22

2022 年更新(适用于 .NET 4.8 和 .NET 6):
特别是当需要多行字符串模板时,C# 6 现在为我们提供了 $ 和 @ 一起使用的功能,如下所示:(

您只需通过将 " 替换为 "" 来转义引号)

string name = "Mike";
int age = 20 + 14; // 34
string product = "milk";

var htmlTemplateContent = $@"
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset=""utf-8"" />
        <title>Sample HTML page</title>
    </head>
    <body>
        Hi {name}, now that you're {age.ToString()}, how do you like {product}?
    </body>
    </html>";

UPDATE for 2022 (for both .NET 4.8 and .NET 6):
Especially when multi-line string templates are needed, C# 6 now offers us both $ and @ used together like:

(You just need to escape quotes by replacing " with "")

string name = "Mike";
int age = 20 + 14; // 34
string product = "milk";

var htmlTemplateContent = $@"
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset=""utf-8"" />
        <title>Sample HTML page</title>
    </head>
    <body>
        Hi {name}, now that you're {age.ToString()}, how do you like {product}?
    </body>
    </html>";
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文