25

If I can format a string using

string.Format("my {0} template {1} here", 1, 2)

can I reverse the process - I provide the template and a filled-in string, .net returns arg0, arg1, etc.?

2
  • 1
    That actually sounds like a case for using a regex I think ... Commented Mar 17, 2011 at 22:41
  • I'm curious if I can avoid regex. It seems that two objects share a template and simply use that populate and de-populate a string. Would be quite elegant. Commented Mar 17, 2011 at 22:42

4 Answers 4

33

There's no elegant way to reverse the formatted string. But you can try this if you want a simple function.

private List<string> reverseStringFormat(string template, string str)
{
     //Handles regex special characters.
    template = Regex.Replace(template, @"[\\\^\$\.\|\?\*\+\(\)]", m => "\\" 
     + m.Value);

    string pattern = "^" + Regex.Replace(template, @"\{[0-9]+\}", "(.*?)") + "$";
        
    Regex r = new Regex(pattern);
    Match m = r.Match(str);

    List<string> ret = new List<string>();

    for (int i = 1; i < m.Groups.Count; i++)
    {
        ret.Add(m.Groups[i].Value);
    }

    return ret;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! Was hoping for something build-in to .net but it seems like there isn't. This is a good solution.
Nice solution. To make sure that the generated pattern is valid, you have to replace all the special regex characters before creating the pattern: template = Regex.Replace(template, @"[\\\^\$\.\|\?\*\+\(\)]", m => "\\" + m.Value).
@Elian Good call. Also, one should note that this will not reproduce the correct result in the case described below by Alexei.
add square braces to escaped characters: template = Regex.Replace(template, @"[\\\^\$\.\|\?\*\+\(\)\[\]]", m => "\\" + m.Value);
8

String.Format is not reversable in general case.

If you have exactly one {0} it is actaully possible to write generic code that at least extract string representation of the value. You definitely can't reverse it to produce original objects.

Samples:

  1. Multiple arguments: string.Format("my{0}{1}", "aa", "aaa"); produces "myaaaaa", tring to reverse string.ReverseFormat("my{0}{1}", "myaaaaa") have to decide how to split "aaaaa" portion in 2 without any information.

  2. Inability to reverse to data type string.Format("{0:yyyy}", DateTime.Now); results in 2011, most of information about value itself lost.

Comments

5

One way to do this would be regular expressions. For your example you could do:

Regex regex = new Regex("^my (.*?) template (.*?) here$");
Match match = regex.Match("my 53 template 22 here");
string arg0 = match.Groups[1].Value;    // = "53"
string arg1 = match.Groups[2].Value;    // = "22"

It wouldn't be hard to write an extension method to do exactly what you want based on this technique.

Just for fun, here's my first naive attempt. I haven't tested this but it should be close.

public static object[] ExtractFormatParameters(this string sourceString, string formatString)
{
    Regex placeHolderRegex = new Regex(@"\{(\d+)\}");
    Regex formatRegex = new Regex(placeHolderRegex.Replace(formatString, m => "(<" + m.Groups[1].Value + ">.*?)");
    Match match = formatRegex.Match(sourceString);
    if (match.Success)
    {
        var output = new object[match.Groups.Count-1];
        for (int i = 0; i < output.Length; i++)
            output[i] = match.Groups[i+1].Value;
        return output;
    }
    return new object[];
} 

This will allow you to do

object[] args = sourceString.ExtractFormatParameters("my {0} template {1} here");

The method is VERY naive and has many problems, but it will basically find any placeholders in the format expression, and find the corresponding text in the source string. It will give you the values corresponding to the placeholders listed from left to right, without reference to ordinal, or any format specified in the placeholder. This functionality could be added.

Another problem is that any special regex characters in the format string will cause the method to fall over. Some more processing of formatRegex would need to be done to escape any special characters that are part of formatString.

3 Comments

Can you confirm that there's no way to use the string template as-is (without writing custom extension method)? see comments above.
Not that I'm aware of, but that doesn't mean much. But I can see writing a method that would take the format and a object array of the right size, parse the format and make it into an appropriate regex, do the match and then populate the object array with the resulting values.
yup...like in @climbage's solution above.
2

Use regular expressions to parse out group matches.

Regex.Match("my (.*) template (.*) here", theFilledInString);

I don't have VS open, so I can't verify if I have the method name right, but you'll know what I mean. By using paranthesis, the returned match result will have groups[0] and groups[1] containing your extracted matches.

2 Comments

Is there a way to avoid regex? Does .net have a build-in mechanism to parse string using templates where args are in {} format?
I haven't seen such a thing. I guess you can auto-replace "{0}" with "(.*)" when handling these templates. Look at climbage's answer above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.