【问题标题】:How to provide custom string placeholder for string format如何为字符串格式提供自定义字符串占位符
【发布时间】:2009-07-09 07:17:57
【问题描述】:

我有一个字符串

string str ="Enter {0} patient name";

我正在使用 string.format 来格式化它。

String.Format(str, "Hello");

现在,如果我还希望从某些配置中检索患者,那么我需要将 str 更改为类似 "Enter {0} {1} name"。因此它将用第二个值替换 {1}。问题是我想要而不是 {1} 一些其他格式,例如 {pat}。但是当我尝试使用时,它会抛出一个错误。我想要不同格式的原因是我需要像这样更改很多文件(可能包含 {0}、{1} 等)。所以我需要一个可以在运行时替换的自定义占位符。

【问题讨论】:

    标签: c# .net string string-formatting


    【解决方案1】:

    您可能想通过James Newton-King 查看FormatWith 2.0。它允许您使用属性名称作为格式化标记,例如:

    var user = new User()
    {
        Name = "Olle Wobbla",
        Age = 25
    };
    
    Console.WriteLine("Your name is {Name} and your age is {Age}".FormatWith(user));
    

    您也可以将它与匿名类型一起使用。

    更新:Scott Hanselman 也有一个类似的solution,但它是作为Object 上的一组扩展方法实现的,而不是String

    2012 年更新:您可以在 NuGet.org 上获取 Calrius Consulting 的 NETFx String.FormatWith Extension Method NuGet 包

    2014 年更新:还有StringFormat.NETlittlebit's StringFormat

    【讨论】:

    • +1。这很有趣,我以前没见过。看起来它适用于匿名类型。
    • 在链接的文章中,一些评论者担心性能。使用前一定要看看性能是否可以接受。
    • 不得不使用 Scott 的版本,因为 FormatWith 似乎依赖于 System.Web。
    【解决方案2】:

    RegexMatchEvaluator 似乎是一个不错的选择:

    static readonly Regex re = new Regex(@"\{([^\}]+)\}", RegexOptions.Compiled);
    static void Main()
    {
        string input = "this {foo} is now {bar}.";
        StringDictionary fields = new StringDictionary();
        fields.Add("foo", "code");
        fields.Add("bar", "working");
    
        string output = re.Replace(input, delegate (Match match) {
            return fields[match.Groups[1].Value];
        });
        Console.WriteLine(output); // "this code is now working."
    }
    

    【讨论】:

      【解决方案3】:

      我看到了上面所有的答案,但是无法正确回答问题:)

      以下代码不符合您的要求有什么特殊原因吗?

      string myFirstStr = GetMyFirstStrFromSomewhere();
      string mySecondStr = GetMySecondStrFromSomewhere();
      
      string result = "Enter " + myFirstStr + " " + mySecondStr + " name";
      

      【讨论】:

      • 通常是因为字符串来自其他地方,例如翻译资源或某种用户可编辑的模板。
      【解决方案4】:
      object[] myInts = new int[] {8,9}; 
      

      但是你可以逃脱:

      object[] myInts = new string[] { "8", "9" }; 
      string bar = string.Format("{0} {1}", myInts); 
      

      【讨论】:

      • 它会起作用,但问题是 OP 不喜欢 {0} 数字占位符并想要一个更具描述性的占位符。
      【解决方案5】:

      这是我在这里找到的另一个版本:http://www.reddit.com/r/programming/comments/bodml/beef_up_params_in_c_5_to_solve_lambda_abuse/c0nrsf1

      对此的任何解决方案都将涉及反射,这不太理想,但这是他的代码,其中解决了其他一些主要的性能问题。 (没有错误检查。如果你喜欢添加它。):

      1) 使用直接运行时反射,没有 DataBinder 开销

      2) 不使用正则表达式,使用单遍解析和状态。

      3) 不会将字符串转换为中间字符串,然后再将其转换为最终格式。

      4) 使用单个 StringBuilder 进行分配和连接,而不是在所有地方新建字符串并将它们连接成新字符串。

      5) 消除为 n 次替换操作调用委托的堆栈开销。

      6) 一般来说,单次通过会以相对线性的方式进行扩展(每个道具查找和嵌套道具查找仍有一些成本,但仅此而已。)

      public static string FormatWith(this string format, object source)
      {
          StringBuilder sbResult = new StringBuilder(format.Length);
          StringBuilder sbCurrentTerm = new StringBuilder();
          char[] formatChars = format.ToCharArray();
          bool inTerm = false;
          object currentPropValue = source;
      
          for (int i = 0; i < format.Length; i++)
          {
              if (formatChars[i] == '{')
                  inTerm = true;
              else if (formatChars[i] == '}')
              {
                  PropertyInfo pi = currentPropValue.GetType().GetProperty(sbCurrentTerm.ToString());
                  sbResult.Append((string)(pi.PropertyType.GetMethod("ToString", new Type[]{}).Invoke(pi.GetValue(currentPropValue, null), null)));
                  sbCurrentTerm.Clear();
                  inTerm = false;
                  currentPropValue = source;
              }
              else if (inTerm)
              {
                  if (formatChars[i] == '.')
                  {
                      PropertyInfo pi = currentPropValue.GetType().GetProperty(sbCurrentTerm.ToString());
                      currentPropValue = pi.GetValue(source, null);
                      sbCurrentTerm.Clear();
                  }
                  else
                      sbCurrentTerm.Append(formatChars[i]);
              }
              else
                  sbResult.Append(formatChars[i]);
          }
          return sbResult.ToString();
      } 
      

      【讨论】:

        【解决方案6】:

        您还可以使用 Marc Gravell 中的示例并扩展 String 类对象:

        public static class StringExtension
        {
            static readonly Regex re = new Regex(@"\{([^\}]+)\}", RegexOptions.Compiled);
            public static string FormatPlaceholder(this string str, Dictionary<string, string> fields)
            {
                if (fields == null)
                    return str;
        
                return re.Replace(str, delegate(Match match)
                {
                    return fields[match.Groups[1].Value];
                });
        
            }
        }
        

        示例用法:

        String str = "I bought a {color} car";
        Dictionary<string, string> fields = new Dictionary<string, string>();
        fields.Add("color", "blue");
        
        str.FormatPlaceholder(fields));
        

        【讨论】:

          【解决方案7】:

          您最好对自定义字段使用替换,对其余部分使用格式,例如:

          string str = "Enter {0} {pat} name";
          String.Format(str.Replace("{pat}", "Patient"), "Hello");
          

          【讨论】:

            【解决方案8】:

            我想要一些更像 Python 的字符串格式的东西,所以我写了这个: https://gist.github.com/samwyse/b225b32ae1aea6fb27ad9c966b9ca90b

            像这样使用它:

            Dim template = New FormatFromDictionary("{cat} vs {dog}")
            Dim d = New Dictionary(Of String, Object) From {
                {"cat", "Felix"}, {"dog", "Rex"}}
            Console.WriteLine(template.Replace(d)) ' Felix vs Rex
            

            【讨论】:

              猜你喜欢
              • 2019-12-25
              • 1970-01-01
              • 2016-03-16
              • 2012-08-28
              • 1970-01-01
              • 2013-08-09
              • 1970-01-01
              • 2015-02-08
              • 2013-08-20
              相关资源
              最近更新 更多