我正在编写一个名为 StringTemplate
的类,它允许像使用 String.Format
一样格式化对象,但使用名称而不是占位符的索引。这是一个例子:
string s = StringTemplate.Format("Hello {Name}. Today is {Date:D}, and it is {Date:T}.",
new { Name = "World", Date = DateTime.Now });
为了实现这个结果,我寻找占位符并将它们替换为索引。然后我将生成的格式字符串传递给 String.Format
。
这工作正常,除非有双大括号,这是一个转义序列。所需的行为(与 String.Format
相同)描述如下:
- "Hello {Name}" 的格式应为 "Hello World"
- "Hello {{Name}}" 的格式应为"Hello {Name}"
- "Hello {{{Name}}}" 的格式应为 "Hello {World}"
- “你好{{{{Name}}}}”的格式应为“你好{{Name}}”
等等……
但我当前的正则表达式不检测转义序列,并且始终将括号之间的子字符串视为占位符,所以我得到类似"Hello {0}" 的内容。
这是我当前的正则表达式:
private static Regex _regex = new Regex(@"{(?<key>\w+)(?<format>:[^}]+)?}", RegexOptions.Compiled);
如何修改此正则表达式以忽略转义的大括号?真正困难的是我应该根据括号的数量是奇数还是偶数来检测占位符...我不能想一个用正则表达式来做的简单方法,它甚至可能吗?
为了完整性,这里是 StringTemplate
类的完整代码:
public class StringTemplate
{
private string _template;
private static Regex _regex = new Regex(@"{(?<key>\w+)(?<format>:[^}]+)?}", RegexOptions.Compiled);
public StringTemplate(string template)
{
if (template == null)
throw new ArgumentNullException("template");
this._template = template;
}
public static implicit operator StringTemplate(string s)
{
return new StringTemplate(s);
}
public override string ToString()
{
return _template;
}
public string Format(IDictionary<string, object> values)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
Dictionary<string, int> indexes = new Dictionary<string, int>();
object[] array = new object[values.Count];
int i = 0;
foreach (string key in values.Keys)
{
array[i] = values[key];
indexes.Add(key, i++);
}
MatchEvaluator evaluator = (m) =>
{
if (m.Success)
{
string key = m.Groups["key"].Value;
string format = m.Groups["format"].Value;
int index = -1;
if (indexes.TryGetValue(key, out index))
{
return string.Format("{{{0}{1}}}", index, format);
}
}
return string.Format("{{{0}}}", m.Value);
};
string templateWithIndexes = _regex.Replace(_template, evaluator);
return string.Format(templateWithIndexes, array);
}
private static IDictionary<string, object> MakeDictionary(object obj)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
foreach (var prop in obj.GetType().GetProperties())
{
dict.Add(prop.Name, prop.GetValue(obj, null));
}
return dict;
}
public string Format(object values)
{
return Format(MakeDictionary(values));
}
public static string Format(string template, IDictionary<string, object> values)
{
return new StringTemplate(template).Format(values);
}
public static string Format(string template, object values)
{
return new StringTemplate(template).Format(values);
}
}
最佳答案
这很可能使用正则表达式 - 但我完全不相信这将是最容易维护的解决方案。鉴于您真的只对这里的大括号和冒号感兴趣(我认为),我个人会避免使用正则表达式。
我会构造一系列标记,每个标记要么是文字,要么是格式字符串。只需沿着绳子行走并注意开括号和闭括号即可构建它。然后评估序列只是连接标记的问题,在适当的地方格式化每个标记。
话又说回来,我从来都不是正则表达式的忠实粉丝 - 只是偶尔它们很棒,但很多时候它们感觉有点矫枉过正。在这种情况下,也许有一些聪明的方法可以让他们做你想做的事......
顺便说一句,您需要定义在大括号未正确匹配的情况下您希望发生什么,例如
{{Name} foo
关于c# - String.Format 类实用程序的正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1445571/