Skip to content

Instantly share code, notes, and snippets.

@dotnetchris
Forked from atifaziz/HenriFormatter.cs
Created March 23, 2012 14:07
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dotnetchris/2170953 to your computer and use it in GitHub Desktop.
Save dotnetchris/2170953 to your computer and use it in GitHub Desktop.
Updated HenriFormatter from Named Formats Redux
// http://haacked.com/archive/2009/01/14/named-formats-redux.aspx#70485
using System;
using System.Text;
using System.Web;
using System.Web.UI;
namespace StringLib
{
public static class HenriFormatter
{
public static string HenriFormat(this string format, object source)
{
if (format == null)
throw new ArgumentNullException("format");
var result = new StringBuilder(format.Length * 2);
var expression = new StringBuilder();
var e = format.GetEnumerator();
while (e.MoveNext())
{
var ch = e.Current;
if (ch == '{')
{
while (true)
{
if (!e.MoveNext())
throw new FormatException();
ch = e.Current;
if (ch == '}')
{
result.Append(OutExpression(source, expression.ToString()));
expression.Length = 0;
break;
}
if (ch == '{')
{
result.Append(ch);
break;
}
expression.Append(ch);
}
}
else if (ch == '}')
{
if (!e.MoveNext() || e.Current != '}')
throw new FormatException();
result.Append('}');
}
else
{
result.Append(ch);
}
}
return result.ToString();
}
private static string OutExpression(object source, string expression)
{
string format = "{0}";
int colonIndex = expression.IndexOf(':');
if (colonIndex > 0)
{
format = "{0:" + expression.Substring(colonIndex + 1) + "}";
expression = expression.Substring(0, colonIndex);
}
try
{
if (String.IsNullOrEmpty(format))
{
if (source is RouteData)
source = ((RouteData) (source)).Values;
var dict = source as IDictionary<string, object>;
return dict != null ? dict[expression].ToString() : (DataBinder.Eval(source, expression) ?? "").ToString();
}
return DataBinder.Eval(source, expression, "{0:" + format + "}") ?? "";
}
catch (HttpException)
{
throw new FormatException();
}
}
}
}
@dotnetchris
Copy link
Author

Add support for anonymous object, RouteData, and RouteValueDictionary

@isamochernov
Copy link

You may consider changing the last parameter to 'Eval' from "{0:" + format + "}" to just 'format' at line 82. It doesn't compile the way it is

@dotnetchris
Copy link
Author

@isamochernov are you targeting the full .NET4 profile? This the exact file I'm using.

@isamochernov
Copy link

Mm, I wasn't precise, the file gets compiled. I just got format exception when I tried to run it across tests.

Please check - you set 'string format = "{0}"' at line 63 and then since it is never empty, the last parameter for DataBinder.Eval becomes {0:{0}} which is definitely not what we want it to be.

@AkosLukacs
Copy link

Strange, but I think too that line #82 should be return DataBinder.Eval(source, expression, format) ?? "";. Simply running "Hi {name}!".HenriFormat(new { name = "Henri" }) throws a FormatException.
Debugging to line #82 the value of the "{0:" + format + "}" expression becomes this: "{0:{0}}"
(on .net 4.5.2)

After the change both of these pass, so format parameters are respected:

"Hi {name}!".HenriFormat(new { name = "Henri" }).Should().Be("Hi Henri!");
"{data:yyyy.MM}".HenriFormat(new { data = new DateTime(2015, 2, 1) }).Should().Be("2015.02");

btw do you still use it after several years?

@dotnetchris
Copy link
Author

@AkosLukacs github notifications are pretty stupid and just flood noise instead of actual important ones. I was using this around the time you posted. I haven't used it this year as i've been doing very high performance webscale development, generally trying to avoid string manipulation entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment