查找. net 's string.Format结果的格式化部分

本文关键字:结果 Format 格式化部 string net 查找 | 更新日期: 2023-09-27 18:12:43

我以数据驱动的方式使用string.Format—我知道有多少对象要格式化,但不知道其他的—并且我想找出结果的哪些部分是格式化的对象,哪些部分来自格式化字符串。(我打算在UI中显示格式化的结果,格式化的部分是"hot",这样可以将它们悬停在上面并单击以激活与产生它们的对象相关的一些UI。)

例如,假设我调用这个假设的格式化函数,传入一个特定的格式字符串,并将(string)"fred"作为对象0,(int)50作为对象1。假设结果是(fred)。我希望能够确定从索引1开始的4个字符是格式化对象0的结果,而对象1没有格式化。(显然,本例中的格式字符串类似于"{0}"。)

是否有一些内置的方法来做到这一点?

(这感觉像一个通用的。net/clr问题-但如果它是相关的,我使用c#)

查找. net 's string.Format结果的格式化部分

如果您只知道格式字符串和结果字符串,但不知道格式化的参数,则不可能在结果字符串中找到它们。

例如,下面几行产生相同的结果:
string.Format("{0}{1}", "a", "bc")
string.Format("{0}{1}", "ab", "c")

您还可以使用正则表达式,更具体地说,使用MatchEvaluator,这样您就可以跟踪这些索引。我做了一个例子,您可以为您的应用程序自定义:

static void Main(string[] args)
{
    var arg0 = (string)"fred";
    var arg1 = (int)50;
    var format = "{0}";
    var result = Format(format, arg0, arg1);
    for(int index = 0; index < result.Arguments.Length; index++)
    {
        if(String.IsNullOrEmpty(result.Arguments[index].Capture))
        {
            Console.WriteLine(
                "Argument {0} with value {1} was unused", 
                index, result.Arguments[index].Value);
        }
        else
        {
            Console.WriteLine(
                "Argument {0} with value {1} was used, starting at index {2}", 
                index, result.Arguments[index].Value,
                result.Arguments[index].Index);
        }
    }
}
static Transformation Format(string format, params object[] args)
{
    var value = new Transformation
    {
        Format    = format,
        Arguments = (args ?? new object[]{})
            .Select (o => new Argument{ Value = o })
            .ToArray()
    };
    value.Result = Regex.Replace(format, @"{('d+)}", (match) =>
    {
        int index = Convert.ToInt32(match.Groups[1].Value);
        if (index > args.Length) return "";
        var @this = args[index];
        var result = @this == null ? "" : @this.ToString();
        value.Arguments[index].Index   = match.Index;
        value.Arguments[index].Capture = match.Value;
        value.Arguments[index].Length  = result.Length;
        return result;
    });
    return value;
}
class Transformation
{
    public string Format        { get; set; }
    public string Result        { get; set; }
    public Argument[] Arguments { get; set; }
}
class Argument
{
    public object Value         { get; set; }
    public int Index            { get; set; }
    public int Length           { get; set; }
    public string Capture       { get; set; }
}

最后我写了我自己的东西,因为听起来好像没有任何内置的方法来获得我想要的东西,无论是从现有的函数还是通过连接到某处的东西。

首先,一个对象用于存储结果中每个对象的字符串被插入的位置:

    public class FormattedStringPart
    {
        public int ObjectIndex { get; private set; }
        public int StartIndex { get; private set; }
        public int Length { get; private set; }
        public FormattedStringPart(int objectIndex, int startIndex, int length)
        {
            ObjectIndex = objectIndex;
            StartIndex = startIndex;
            Length = length;
        }
    }

然后函数本身通过格式字符串工作,建立一个StringBuilder。它要么逐字添加字符,要么找到一个格式化部分,用string.Format对其进行格式化,并跟踪索引,以便为插入创建一个新的FormattedStringPart。(使这变得非常简单的关键是,您可以将格式部分和object的整个数组交给string.Format -因此无需仔细查看格式部分以检查有效性。把它传递给string.Format,看看会发生什么。)

    public static string FormatString(string format, 
                                      object[] args, 
                                      out FormattedStringPart[] formattedParts)
    {
        var parts = new List<FormattedStringPart>();
        var result = new StringBuilder();
        int i = 0;
        while (i < format.Length)
        {
            char c = format[i];
            if (c == '{')
            {
                int j = format.IndexOf('}', i);
                if (j < 0)
                    throw new FormatException("Missing '}'");
                int startIndex = result.Length;
                result.AppendFormat(format.Substring(i, (j - i) + 1), args);
                ++i;
                // the AppendFormat call should have ensured there's a
                // valid number following...
                int objectIndex = 0;
                while (format[i] >= '0' && format[i] <= '9')
                {
                    objectIndex *= 10;
                    objectIndex += (int)(format[i] - '0');
                    ++i;
                }
                parts.Add(new FormattedStringPart(objectIndex, 
                                                  startIndex, 
                                                  result.Length - startIndex));
                i = j + 1;
            }
            else
            {
                result.Append(c);
                ++i;
            }
        }
        if (parts.Count == 0)
            formattedParts = null;
        else
            formattedParts = parts.ToArray();
        return result.ToString();
    }

眼尖的string.Format粉丝会注意到这与string.Format并不完全相同-这在我的情况下并不重要。