Giacomo Pirinoli
Giacomo Pirinoli

Reputation: 568

C# - Auto-detect Escape and Argument curly braces in string.Format

starting from this C# example:

string myStringFormat = "I want to surround string {0} with {{ and }}";
string myStringArgs = "StringToSurround";
string myFinalString = string.Format(myStringFormat, myStringArgs);

I'd like to know if there is a quick and simple way to distinguish between escape character/sequence and arguments for curly braces/brackets.

The reasons why I am asking this are:
+] I want to provide some logging functionality and I don't want to require users to be aware of the double curly braces/brackets escape rule
+] I want to be very fast in doing this distinction for performance requirements

Currently the only solution I can think about is to scan the string looking for curly braces/brackets and do some check (number parsing) on subsequent characters. Probably regex can be helpful but I cannot find a way to use them in this scenario.

Btw, the final situation I'd like to achieve is user being allowed to this without getting exceptions:

string myStringFormat = "I want to surround string {0} with { and }";
string myStringArgs = "StringToSurround";
//string myFinalString = string.Format(myStringFormat, myStringArgs); throwing exception
string myFinalString = MyCustomizedStringFormat(myStringFormat, myStringArgs);

EDIT:

sorry the word "surround" was tricky and misleading, please consider this example:

string myStringFormat = "I want to append to string {0} these characters {{ and }}";
string myStringArgs = "StringToAppendTo";
string myFinalString = string.Format(myStringFormat, myStringArgs);

giving output
I want to append to string StringToAppendTo these characters { and }

Upvotes: 1

Views: 521

Answers (1)

Bizhan
Bizhan

Reputation: 17115

Use this Regex to find the Argument substrings:

{\d+}

This regex escapes {} {1a} etc. and only chooses {1} {11} etc.


Now you need to handle either Arguments (replace them with their values) or the Escaped curly braces (replace them with double braces). The choice is yours and it depends on the number of occurrences of each case. (I chose to replace arguments in my code below)

Now you need to actually replace the characters. Again the choice is yours to use a StringBuilder or not. It depends on the size of your input and the number of replacements. In any case I suggest StringBuilder.

var m = Regex.Matches(input, @"{\d+}");
if (m.Any())
{
    // before any arg
    var sb = new StringBuilder(input.Substring(0, m[0].Index));

    for (int i = 0; i < m.Count; i++)
    {
        // arg itself
        sb.Append(args[i]);

        // from right after arg 
        int start = m[i].Index + m[i].Value.Length;

        if (i < m.Count - 1)
        {
            // until next arg
            int length = m[i + 1].Index - start;
            sb.Append(input.Substring(start, length));
        }
        else
            // until end of input
            sb.Append(input.Substring(start));
    }
}

I believe this is the most robust and cleanest way to do it,and it does not have any performance (memory or speed) issues.


Edit

If you don't have access to args[] then you can first replace {/} with {{/}} and then simply do these modifications to the code:

  1. use this pattern: @"{{\d+}}"
  2. write m[i].Value.Substring(1, m[i].Value.Length - 2) instead of args[i]

Upvotes: 1

Related Questions