如何为字符串格式提供自定义字符串占位符

How to provide custom string placeholder for string format

提问人: 提问时间:7/9/2009 最后编辑:Shiridish 更新时间:3/18/2022 访问量:67130

问:

我有一根绳子

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

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

String.Format(str, "Hello");

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

C# .NET 字符串 字符串格式

评论


答:

21赞 Marc Gravell 7/9/2009 #1

Regex似乎是一个不错的选择:MatchEvaluator

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."
}
56赞 Magnus Lindhe 7/9/2009 #2

您可能想看看 James Newton-KingFormatWith 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 也有一个类似的解决方案,但它是作为一组扩展方法实现的,而不是 .ObjectString

2012 年更新:您可以在 NuGet.org 上获取 Calrius Consulting 的 NETFx String.FormatWith 扩展方法 NuGet 包

2014 年更新:还有 StringFormat.NETlittlebit 的 StringFormat

评论

1赞 RichardOD 7/9/2009
+1.这很有趣,我以前从未见过。看起来它适用于匿名类型。
0赞 Janis Veinbergs 10/5/2011
在链接的文章中,一些评论者担心性能。使用前一定要看看性能是否可接受。
0赞 Bryan Legend 2/19/2013
不得不使用 Scott 的版本,因为 FormatWith 似乎依赖于 System.Web。
1赞 Dean Povey 7/9/2009 #3

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

string str = "Enter {0} {pat} name";
String.Format(str.Replace("{pat}", "Patient"), "Hello");
2赞 Chansik Im 7/9/2009 #4

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

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

string myFirstStr = GetMyFirstStrFromSomewhere();
string mySecondStr = GetMySecondStrFromSomewhere();

string result = "Enter " + myFirstStr + " " + mySecondStr + " name";

评论

0赞 Oylex 3/27/2018
通常是因为字符串来自其他地方,例如翻译资源或某种用户可编辑的模板。
0赞 john cs 1/13/2023
这段代码很糟糕,如果它是一个更复杂的字符串呢?你要连接一堆东西吗?
3赞 mujtaba Hyder 2/23/2012 #5
object[] myInts = new int[] {8,9}; 

但是,您可以侥幸逃脱:

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

评论

1赞 Hans Kesting 2/24/2012
它会起作用,但问题是 OP 不喜欢数字占位符,想要一个更具描述性的占位符。{0}
2赞 Bryan Legend 2/19/2013 #6

这是我在这里找到的另一个版本: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();
} 
2赞 Ole K 8/19/2014 #7

您还可以使用 Marc Gravell 和 Extend the 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));
0赞 samwyse 8/31/2017 #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
1赞 MagogCZ 3/18/2022 #9
var user = new User()
{
    Name = "John",
    Age = 21
};
String result = $"Your name is {user.Name} and your age is {user.Age}";