【问题标题】:named String.Format, is it possible?命名为String.Format,有可能吗?
【发布时间】:2009-06-18 00:06:23
【问题描述】:

我不想使用{0} {1} 等。我想改用{title}。然后以某种方式填写该数据(下面我使用了Dictionary)。此代码无效并引发异常。我想知道我是否可以做一些类似于我想要的事情。使用{0 .. N} 不是问题。我只是好奇。

Dictionary<string, string> d = new Dictionary<string, string>();
d["a"] = "he";
d["ba"] = "llo";
d["lol"] = "world";
string a = string.Format("{a}{ba}{lol}", d);

【问题讨论】:

标签: c# string.format


【解决方案1】:

不,但是这个扩展方法可以做到

static string FormatFromDictionary(this string formatString, Dictionary<string, string> valueDict) 
{
    int i = 0;
    StringBuilder newFormatString = new StringBuilder(formatString);
    Dictionary<string, int> keyToInt = new Dictionary<string,int>();
    foreach (var tuple in valueDict)
    {
        newFormatString = newFormatString.Replace("{" + tuple.Key + "}", "{" + i.ToString() + "}");
        keyToInt.Add(tuple.Key, i);
        i++;                    
    }
    return String.Format(newFormatString.ToString(), valueDict.OrderBy(x => keyToInt[x.Key]).Select(x => x.Value).ToArray());
}

【讨论】:

  • 小心,formatString 中有“{{thing}}”,ValueDict 中有一个名为“thing”的键将替换字符串中的“thing”作为数字。
  • 这很好用。 ReSharper 不喜欢将字符串转换为对象,因此我将您的字典更改为字符串、对象对。另外,一个挑剔的但我不同意键值对是一个元组。它并不是真正的有序列表。 ValueDict 更像是一个元组。
【解决方案2】:

勾选此项,支持格式化:

    public static string StringFormat(string format, IDictionary<string, object> values)
    {
        var matches = Regex.Matches(format, @"\{(.+?)\}");
        List<string> words = (from Match matche in matches select matche.Groups[1].Value).ToList();

        return words.Aggregate(
            format,
            (current, key) =>
                {
                    int colonIndex = key.IndexOf(':');
                    return current.Replace(
                        "{" + key + "}",
                        colonIndex > 0
                            ? string.Format("{0:" + key.Substring(colonIndex + 1) + "}", values[key.Substring(0, colonIndex)])
                            : values[key].ToString());
                });
    }

使用方法:

string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var dictionary = new Dictionary<string, object>
    {
        { "foo", 123 },
        { "bar", true },
        { "baz", "this is a test" },
        { "qux", 123.45 },
        { "fizzle", DateTime.Now }
    };
StringFormat(format, dictionary)

【讨论】:

  • 不考虑转义:StringFormat("{{key}}", new Dictionary&lt;string, object&gt;(){["key"] = "value"}); 失败。
【解决方案3】:

您可以实现自己的:

public static string StringFormat(string format, IDictionary<string, string> values)
{
    foreach(var p in values)
        format = format.Replace("{" + p.Key + "}", p.Value);
    return format;
}

【讨论】:

  • 这会失去很多 String.Format 的功能。
  • 请注意,使用此功能您不能多次重复该键。例如,Hi {{Name}}, is your name really {{Name}}?
  • 我认为@Fred 是错误的。 String.Replace 将替换每个 {name} docs.microsoft.com/en-us/dotnet/api/…
  • 如果您希望经常将其用于较长的字符串,则使用 StringBuilder 会产生更好的性能。
【解决方案4】:

Phil Haack 不久前在他的博客上讨论了几种执行此操作的方法:http://haacked.com/archive/2009/01/14/named-formats-redux.aspx。我在两个项目中使用了“Hanselformat”版本,没有任何抱怨。

【讨论】:

  • 我也喜欢 Hansel 格式化程序。它不依赖于 System.Web,就像其他格式化程序一样,它非常适合插入到我的实用程序库中。我喜欢我可以从中获取任何对象和格式。
【解决方案5】:

现在可以了

使用 C# 6.0 的 Interpolated Strings,您可以这样做:

string name = "John";
string message = $"Hi {name}!";
//"Hi John!"

【讨论】:

  • 这不是我们所要求的。如果您不知道该字段的名称,则无法准备好具有该标识符的变量。
  • 你可以做 `$"Hi {d["name"]}" 这应该符合 OP 的要求
  • 您无法使用字符串插值对格式字符串进行参数化。 eg:var format = "时间是 {DateTime.Now}"; Console.WriteLine($format);
  • @Kasun format = $"Time is {DateTime.Now}";您从字符串的开头错过了 $。
【解决方案6】:
static public class StringFormat
{
    static private char[] separator = new char[] { ':' };
    static private Regex findParameters = new Regex(
        "\\{(?<param>.*?)\\}",
        RegexOptions.Compiled | RegexOptions.Singleline);

    static string FormatNamed(
        this string format,
        Dictionary<string, object> args)
    {
        return findParameters.Replace(
            format,
            delegate(Match match)
            {
                string[] param = match.Groups["param"].Value.Split(separator, 2);

                object value;
                if (!args.TryGetValue(param[0], out value))
                    value = match.Value;

                if ((param.Length == 2) && (param[1].Length != 0))
                    return string.Format(
                        CultureInfo.CurrentCulture,
                        "{0:" + param[1] + "}",
                        value);
                else
                    return value.ToString();
            });
    }
}

比其他扩展方法涉及更多,但这也应该允许在它们上使用非字符串值和格式模式,所以在你原来的例子中:

Dictionary<string, object> d = new Dictionary<string, object>();
d["a"] = DateTime.Now;
string a = string.FormatNamed("{a:yyyyMMdd-HHmmss}", d);

也可以...

【讨论】:

    【解决方案7】:

    自 C# 6 发布以来,您可以使用 String Interpolation feature

    解决您问题的代码:

    string a = $"{d["a"]}{d["ba"]}{d["lol"]}";
    

    【讨论】:

      【解决方案8】:

      为什么是字典?这是不必要的,而且过于复杂。一个简单的名称/值对二维数组也可以工作:

      public static string Format(this string formatString, string[,] nameValuePairs)
      {
          if (nameValuePairs.GetLength(1) != 2)
          {
              throw new ArgumentException("Name value pairs array must be [N,2]", nameof(nameValuePairs));
          }
          StringBuilder newFormat = new StringBuilder(formatString);
          int count = nameValuePairs.GetLength(0);
          object[] values = new object[count];
          for (var index = 0; index < count; index++)
          {
              newFormat = newFormat.Replace(string.Concat("{", nameValuePairs[index,0], "}"), string.Concat("{", index.ToString(), "}"));
              values[index] = nameValuePairs[index,1];
          }
          return string.Format(newFormat.ToString(), values);
      }
      

      调用上面的代码:

      string format = "{foo} = {bar} (really, it's {bar})";
      string formatted = format.Format(new[,] { { "foo", "Dictionary" }, { "bar", "unnecessary" } });
      

      结果:"Dictionary = unnecessary (really, it's unnecessary)"

      【讨论】:

      • 有没有理由把key换成int,然后做字符串格式,而不是直接把key换成value?
      【解决方案9】:
      public static string StringFormat(this string format, IDictionary<string, object> values)
      {
          return Regex.Matches(format, @"\{(?!\{)(.+?)\}")
                  .Select(m => m.Groups[1].Value)
                  .Aggregate(format, (current, key) =>
                  {
                      string[] splits = key.Split(":");
                      string replacement = splits.Length > 1
                          ? string.Format($"{{0:{splits[1]}}}", values[splits[0]])
                          : values[key].ToString();
                      return Regex.Replace(current, "(.|^)("+ Regex.Escape($"{{{key}}}")+")(.|$)", 
                                           m => m.Groups[1].ToString() == "{" && m.Groups[3].ToString() == "}"
                                              ? m.Groups[2].ToString()
                                              : m.Groups[1] + replacement + m.Groups[3]
                                          );
                  });
      }
      

      这类似于另一个答案,但它考虑使用 {{text}} 转义。

      【讨论】:

        【解决方案10】:

        这是一个很好的解决方案,在格式化电子邮件时非常有用:http://www.c-sharpcorner.com/UploadFile/e4ff85/string-replacement-with-named-string-placeholders/

        已编辑:

        public static class StringExtension  
        {  
            public static string Format( this string str, params Expression<Func<string,object>>[] args)  
            {  
                var parameters = args.ToDictionary( e=>string.Format("{{{0}}}",e.Parameters[0].Name), e=>e.Compile()(e.Parameters[0].Name));  
        
                var sb = new StringBuilder(str);  
                foreach(var kv in parameters)  
                {  
                    sb.Replace( kv.Key, kv.Value != null ? kv.Value.ToString() : "");  
                }  
        
                return sb.ToString();  
            }  
        }
        

        示例用法:

        public string PopulateString(string emailBody)  
        {  
          User person = _db.GetCurrentUser();  
          string firstName = person.FirstName;    //  Peter  
          string lastName = person.LastName;      //  Pan  
          return StringExtension.Format(emailBody.Format(  
            firstname => firstName,  
            lastname => lastName  
          ));   
        } 
        

        【讨论】:

          【解决方案11】:

          (你的 Dictionary + foreach + string.Replace) 包裹在子例程或扩展方法中?

          显然未优化,但是...

          【讨论】:

            猜你喜欢
            • 2013-11-30
            • 2011-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多