Create a gist now

Instantly share code, notes, and snippets.

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
Owner

Add support for anonymous object, RouteData, and RouteValueDictionary

@isamochernov

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
Owner

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

@isamochernov

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

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
Owner

@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