查找.NET的string.Format结果的格式化部分

时间:2014-07-12 23:12:24

标签: c# .net string.format

我正在以数据驱动的方式使用string.Format - 我知道要格式化的对象有多少,但没有别的 - 我想知道结果的哪些部分是格式化对象和哪些部分从格式字符串逐字逐句。 (我打算在UI中显示带有格式化部分“hot”的格式化结果,以便可以将它们悬停在上面并单击以激活与生成它们的对象相关的一些UI。)

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

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

(这感觉就像一个通用的.NET / CLR问题 - 但如果它是相关的,我正在使用C#。)

3 个答案:

答案 0 :(得分:3)

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

例如,以下行产生相同的结果:

string.Format("{0}{1}", "a", "bc")
string.Format("{0}{1}", "ab", "c")

答案 1 :(得分:2)

您还可以使用正则表达式,更具体地说是使用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; }
}

答案 2 :(得分:0)

最后我写了自己的东西,因为它听起来没有任何内置的方式来获得我想要的东西,无论是来自现有的功能还是挂钩到某个地方。

首先,在结果中存储位置的对象,其中插入了每个对象的字符串:

    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不完全相同 - 在我的情况下这不是(但是?)很重要。