Solved

Need a regex to extract all parameters from string.Format text

Posted on 2011-03-03
7
406 Views
Last Modified: 2012-08-13
I want to create a SafeStringFormat which makes sure the parameter count matches the embedded parameters in the string text for a String.Format.

I'm guessing regex is the most efficient way to go; could somebody give me the code for extracting the parameters?

Something like this:

public string SafeStringFormat(string text, params object[] parms) {

    string[] embeddedParameters = <use regex to extract parameters from text>
    foreach (object o in parms) {
         if (<o doesn't corresponds at least one of the parameters in embeddedParameters>) {
             <throw exception saying that parm[#] is not implemented in <text>>
         }
     }
     foreach (string s in embeddedParameters) {
         if (<s doesn't correspond to one of the objects in parms>) {
             <throw exception saying s is an invalid parameter>
         }
     }
     return string.Format(text, parms);
}

It's the
string[] embeddedParameters = <use regex to extract parameters from text>
part of the code that I need help with...
0
Comment
Question by:FrancineTaylor
  • 3
  • 2
  • 2
7 Comments
 
LVL 7

Expert Comment

by:jdavistx
ID: 35031265
I would think a regex as simple as
(\{\d+\})

Open in new window

would give you your matches, but you'd have to also validate that your numbers are parameter numbers are sequential, and start at 0.

Not to be unhelpful, but I believe you'll get a compiler error if you try to use the normal String.Format() with invalid parameters.  You aren't really making it "safe" with what you've set out initially.

Also, I would think that you wouldn't want to throw exceptions in this way.  I've always considered Exceptions as things that are... exceptions, and not situations that can't be appropriately handled.  An exception would be some program-catastrophic event that no code would be able to accommodate.  In your sample, you could perform preemptive tests that would circumvent those "exceptions" from ever occurring.
0
 
LVL 7

Expert Comment

by:jdavistx
ID: 35031277
If only I could edit!

I would think a regex as simple as

1:

      

(\{\d+\})

Toggle HighlightingOpen in New WindowSelect All

would give you your matches, but you'd have to also validate that your parameter numbers are sequential, and start at 0.

Not to be unhelpful, but I believe you'll get a compiler error if you try to use the normal String.Format() with invalid parameters.  You aren't really making it "safe" with what you've set out initially.

Also, I would think that you wouldn't want to throw exceptions in this way.  I've always considered Exceptions as things that are... exceptions, and not situations that can be appropriately handled.  An exception would be some program-catastrophic event that no code would be able to accommodate.  In your sample, you could perform preemptive tests that would circumvent those "exceptions" from ever occurring.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35031357
I think "[^\{]\{(\d+)[^}]*}[^}]" will work better - as you need to account for two consecutive curly-braces being treated as an escape, meaning {{0} or {0}} should not be treated as a parameter position.

I also know String.Format will throw an exception if there aren't enough objects to fill all the {0} placeholders, but the reverse of that is not true, e.g. String.Format("{0}", "one", "two", "three") will not raise exception.

I think it is also appropriate to raise an exception here, since this method should handle just it's assigned task and not need to worry about how or if to correct the error.  The code calling this method should wrap it in a try/catch block and handle errors there if necessary.

The numbers in the {0} placeholders don't need to be sequential, although the parameter array must have at least N+1 elements, where N is the highest numbered placeholder.

Regex regex = new Regex(@"[^\{]\{(\d+)[^}]*}[^}]");

string test = "Hello {0}, {{ , {1:D}, }}, {{3}, {4}}";
MatchCollection matches = regex.Matches(test);

Open in new window

0
Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35031472
On exceptions...here the calling code handles the exception - you wouldn't want for SafeStringFormat to be showing message boxes and such.  SafeStringFormat might end up in a class library, in which case you have no idea how exceptions should be handled in future circumstances.

Alternatively, you could have the SafeStringFormat return a bool or int instead of raising an exception - though I think raising the exception would be more in line with the preferred .Net model.

Also maybe consider one method that raises exceptions, and another that returns a bool - a la Int32.Parse() and Int32.tryParse()

static void Main()
{
retry:
  try
  {
    SafeStringFormat("Hello {0}", "one", "two", "three");
  }
  catch (InvalidFormatException ex)
  {
    if (MessageBox.Show("Invalid format, try again?") == DialogResult.Yes)
      goto retry;
  }
}

static void SafeStringFormat(string format, params object[] args)
{
  if (args.Length > NumberOfPlaceHolders)
    throw new InvalidFormatException("Not enough placeholders.");
  else if ( args.Length < NumberOfPlaceHolders)
    throw new InvalidFormatException("Too many placeholders.");
}

Open in new window

0
 
LVL 1

Author Comment

by:FrancineTaylor
ID: 35039282
Okay, here is the code I used to test the regex:

            MatchCollection matches = regex.Matches(text);
            List<string> list = new List<string>();
            foreach (Match match in matches) {
                list.Add(match.Groups[1].Value);
            }
            string[] embeddedParameters = list.ToArray();

When I used it with jdavistx's regex ("(\{\d+\})"), I got the correct result from these strings:
    "This has four parms {0}{1}{2}{3}"
    "out of sequence types {42}{99}"
...but these were not extracted correctly:
    "{0:D}"
    "{0,10:C}"
    "Hello {0}, {{ , {1:D}, }}, {{3}, {4}}"

tgerbert's regex ("[^\{]\{(\d+)[^}]*}[^}]") didn't appear to extract any of them correctly.

Is that an accurate test, or am I using the MatchCollection incorrectly?
0
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 500 total points
ID: 35039537
Regex's are tricky.  This is closer, but still not quite right:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

class Program
{
	static void Main(string[] args)
	{
		string tests = @"This has four parms {0}{1}{2}{3}
out of sequence types {42}{99}
...but these were not extracted correctly:
{0:D}
{0,10:C}
Hello {0}, {{ , {1:D}, }}, {{3}, {4}}";

		foreach (string s in tests.Split(new string[] { "\r\n" }, StringSplitOptions.None))
			ShowParameterInfo(s);

		Console.Write("Press any key to exit...");
		Console.ReadKey();
	}

	static void ShowParameterInfo(string formatString)
	{
		Regex regex = new Regex(@"[.-[\{]]*\{(\d+)([^}]*)}[.-[}]]*");

		MatchCollection matches = regex.Matches(formatString);

		Console.WriteLine("Input string: {0}", formatString);
		Console.WriteLine("Parameter count: {0}", matches.Count);

		foreach (Match m in matches)
		{
			Console.WriteLine("\tParameter number: {0}, additional parameter details: {1}",
				m.Groups[1].Value, m.Groups[2].Value);
		}

		Console.WriteLine();
	}
}

Open in new window


Using String.Format("{{0}", "Hello") is invalid, but String.Format("{{{0}", "Hello") and would produce {Hello.  I'm not quite sure how to ensure that a { doesn't immediately precede {0}, unless {{ precede's the {0}, using regular expressions...
0
 
LVL 1

Author Closing Comment

by:FrancineTaylor
ID: 35200208
That works perfectly!  Thanks, tgerbert...
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now