【问题标题】:String.Format with curly braces带花括号的 String.Format
【发布时间】:2011-12-29 07:28:38
【问题描述】:

我们的低级日志库必须处理发送给它的各种日志消息。

其中一些消息包含花括号(作为文本的一部分),一些包含要使用 String.Format 格式化为字符串一部分的参数

例如,这个字符串可以是 Logger 类的输入:

“参数:{主机名}值:{0}” 发送正确的变量以供格式化程序使用。

为了正确执行此操作,我必须转义不属于格式的花括号(通过将它们加倍)。

我想过使用正则表达式来做,但这并不像看起来那么简单,因为我不知道如何在花括号内匹配这些字符串(String.Format 不用于格式化目的的字符串) .

另一个问题是 Logger 类应该尽可能提高性能,开始处理正则表达式作为其操作的一部分可能会影响性能。

是否有任何适当且已知的最佳实践?

【问题讨论】:

  • 您已经在说您将它们加倍(例如:{{Hostname}}。为什么这不起作用?
  • 我没有说我要加倍。我说我必须转义(加倍)与格式无关的那些,或者做其他事情来防止这个问题。

标签: c# string string-formatting curly-braces


【解决方案1】:

只用一个正则表达式:

string input = "Parameter: {Hostname} Value: {0}";
input = Regex.Replace(input, @"{([^[0-9]+)}", @"{{$1}}");
Console.WriteLine(input);

输出:

参数:{{Hostname}} 值:{0}

这当然只适用于没有任何包含数字但仍应使用{{ }} 转义的参数

【讨论】:

  • 效果很好,我会分析一下我们实际上对性能造成了多大影响,但看起来不错。
  • 在包含“P: {Host} Val: {4}”重复的 100 kB 文件上进行了尝试,耗时约 12 毫秒。当然是 YMMV,但我找不到更简洁的方法。
  • 这将一直持续到重新设计当前记录器。非常感谢,我不太确定正则表达式的所有匹配细节,无论如何都需要加强该区域。再次感谢。
  • 如果大括号在一行打开,然后在另一行关闭,则不起作用,例如跨多行的函数。
【解决方案2】:

我认为您应该查看您的记录器界面。与Console.WriteLine 的工作方式进行比较:

  • Console.WriteLine(String) 准确输出给定的字符串,没有格式,{ 和 } 没有什么特别之处。
  • Console.WriteLine(String, Object[]) 使用格式化输出。 { 和 } 是调用者必须转义到 {{ 和 }}
  • 的特殊字符

我认为这是有缺陷的设计,必须区分代码中不同的大括号出现以找出其含义。将应该在输出中出现的 { 转义的负担放到 {{.

【讨论】:

  • 这是完全合理的。我们将两者分开,但是我不相信在这种情况下将责任归咎于调用者是可能的,因为我无法执行此策略(我无法强制记录器的用户自己转义字符串)。
  • 如果假设用户有足够能力理解 {0} 是一个格式化选项,我认为可以安全地假设用户也可以将 { 转义为 {{。我认为最好遵循其他格式感知 API 使用的原则,而不是发明一些特殊的东西。
  • 我同意安德斯的观点。如果他们想要格式化,您应该要求调用者进行转义。作为中途,你可以按照 Anders 的建议实现一个 Log(string), Log(string, params object[]) 并提供一个 Log(bool, string, object[]) 来进行正则表达式解析 - 但标记它带有 [Obsolete] 属性以阻止其使用。
【解决方案3】:

我会将所有花括号加倍,然后我会寻找那些被替换为像{{\d+}} 这样的正则表达式,以便它们恢复到原来的格式 -- {{0}} => {0} --在您的字符串中。
所以对于每一行我都会这样做

string s = input.Replace("{", "{{").Replace("}", "}}");
return Regex.Replace(s, @"{{(?<val>\d+)}}", 
                     m => { return "{" + m.Groups["val"] + "}"; }));

所以这是对原始问题的技术性回答,但@Anders Abel 完全正确。值得再次考虑设计...

【讨论】:

  • 这也无法发现包含格式信息的格式占位符,例如“FourDigitHexValue={0:x4} 日期={0:dd/MM/yyyy}”。有关更多示例,请参阅blog.stevex.net/string-formatting-in-csharp
  • @Grmh 当然。这个答案只是一个快速的解决方法,因为它取决于发送到记录器的输入。但正如 Anders Abel 所说,与其进一步使用更复杂的正则表达式,不如进行另一次设计审查
【解决方案4】:

允许调用者拥有格式化的字符串并处理格式化说明符,例如

Logger.Log("{0:dd/mm/yyy} {0:hh:mm:ss} {hostname} 在 {123Component} 上发生了一些错误 {1:x4}!", DateTime.UtcNow, 257)

你需要一个像这样的正则表达式:

string input = "{0:dd/mm/yyy} {0:hh:mm:ss} {hostname} Some error {1:x4} happened on {123Component}!";
Regex reg = new Regex(@"(\{[^[0-9}]+?[^}]*\}|\{(?![0-9]+:)[^}]+?\})");
string output = reg.Replace(input, "{$1}");
Console.WriteLine(output);

这个输出:

"{0:dd/mm/yyy} {0:hh:mm:ss} {{hostname}} Some error {1:x4} happened on {{123Component}}!"

但重申一下,我同意 Anders Abel 的观点,即您应该重新设计以避免日志库需要这样做。

【讨论】:

    猜你喜欢
    • 2011-10-30
    • 2022-11-30
    • 2012-01-10
    • 2019-01-26
    • 2015-12-19
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多