c# - String.Format 类实用程序的正则表达式

标签 c# regex formatting templates

我正在编写一个名为 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/

相关文章:

javascript - 如何将html代码转换为字符串,用\n替换换行符,用\t替换制表符

c++ - 应该使用什么选项来使用 astyle 删除多余的空格?

c# - 循环中的高效反射

javascript - 否定集后的正则表达式匹配词

java - 用于标准化英国电话号码的正则表达式

java - DateTimeFormatter 自动更正无效(语法上可能)的日历日期

c# - 滚动窗口中的 Gtk Expander : The width of the scrolledwindow doesn't update

java - Java 和 .Net 4.6 为同一 key 生成不同的 JSON Web 加密 (JWE) token

c# - HTML敏捷包: Get whole HTML document as string

javascript - 第 1 组正则表达式匹配的 matchAll 数组