Click here to Skip to main content
Click here to Skip to main content

Custom String Formatting in .NET

By , 28 Apr 2004
 

Introduction

Formatting string output is difficult to avoid in many applications, even with modern graphical user interfaces. It's inevitable that at some point you'll need to format data in a way that's easy to understand. Practically every runtime has its set of string formatting procedures and the .NET Framework is no exception. Understanding how the culture-aware and extensible string formatting works in the .NET Framework, though, can help you create better ways of formatting data.

This article is not so much about the format providers present in the .NET Framework Class Library - though they will be discussed briefly - but about how to write custom formatting routines in such a way that they can easily be used anywhere they're required. Basic string formatting will be discussed, as well as format providers in the .NET Framework, extending existing types with custom format providers, and extending your types with custom formatting.

String Formatting Made Simple

There are countless ways to format strings in any language. It could be as simple as converting all data to strings and concatenating the strings, or using inline expressions with possible format specifiers.

Take, for example, Perl. This simple example uses a local string variable in its print function, as well as a real number that would use the appropriate decimal separator based on the LC_CTYPE environment variable.

use locale;
my $var1 = "Perl";
my $var2 = 5.6;
print("Hello, from $var1 $var2!");

The print function simply evaluates the variables in the expression and writes it to STDOUT (the standard output handle). The current locale changes the format very little without having to pass the variables into other functions first. These inline expressions also do not easily allow the developer to use different expressions while evaluating the same variables.

The ANSI C printf function solves this problem by separating the string expression from the variables to be formatted using a variable parameter list and indexed format specifiers.

char* var1 = "ANSI C";
float var2 = 7.1;
printf("Hello, from %s %g!", var1, var2);

The printf function is also subject to the same locale environment variables as Perl. This ANSI C example shows how a separate string can be used to format the variable list of arguments. This facilitates loading different format strings, including format strings in a different language. The format specifiers additionally gives you a little more control over how the variables are evaluated, such as printing integers in hexadecimal notation, though hese format specifications are pretty inflexible.

In both examples above, string formatting is rather inflexible using the standard runtime libraries. Other solutions exist that use third party libraries or user-defined functions to format arguments before they are evaluated, but it would be nice to have an extensible formatting solution provided by the runtime without having to support third-party code or calling user-defined functions before formatting your arguments.

In the .NET Framework, there are several ways to format inline string expressions, including String.Format[^], StringBuilder.AppendFormat[^], and TextWriter.Write[^] (and WriteLine[^]), which the Console[^] class inherits. These methods not only allow you to load different string expressions to format, but allow you to control the format of a variable list of arguments using a variety of format specifiers provided in the Framework Class Library (FCL) as well as custom format specifiers, which will be covered later. The following example uses the CultureInfo from Thread.CurrentCulture to format relevant variables.

string var1 = ".NET";
float var2 = 1.1;
string.Format("Hello, from {0} {1}!", var1, var2);

Also notice that the format specifiers are also indexed, meaning that you can evaluate parameters regardless of the order in the variable parameter list. You could even re-use the same variable repeatedly in an expression. This also looks much nicer and is more extensible - such that string format expressions could be loaded from a resource - than another common approach that was mentioned earlier in this article:

string var1 = ".NET";
float var2 = 1.1;
string s = "Hello, from " + var1 + " " + var2.ToString() + "!";

I'm sure you'll agree that the string format expression is much easier to read and change at runtime. The true power of formatting strings in .NET doesn't stop there, however. Additional format specifications can be used to control the output of numeric types in endless ways.

Format Specifications in .NET

When using methods like String.Format, indexed placeholders are evaluated and formatted using format specifications in the string format expression. This is known as Composite Formatting[^]. Each format specifier can take the form of {index[,alignment][:formatString]}. These format strings can be just about anything and can define additional options for a specific format. You can also pad and align the formatted output string to fit a certain number of spaces or tabs.

These format specifiers are passed by the StringBuilder internally to implementations of IFormattable, or ICustomFormatter when an IFormatProvider is passed as the first argument (for methods in the FCL). This will be covered shortly.

Format Providers in the Framework Class Library

Format providers - as the name implies - provide formatting capabilities to types. The two format providers in the FCL are the DateTimeFormatInfo[^] and NumberFormatInfo[^] classes. Both implement the IFormatProvider interface, which is an interface that must be supported for format providers when used with the string formatting methods described earlier. This defines a contract by which all format providers must abide so that a common implementation can be established. This interface will be covered in greater detail later.

When formatting numeric types and the DateTime structure, if no IFormatProvider implementation is provided, the two format provider classes above are used automatically to provide formatting for the related types. A simple example using a DateTime follows.

DateTime now = DateTime.Now;
string.Format("Short date:         {0:d}", now);
string.Format("Long date:          {0:D}", now);
string.Format("Sortable date/time: {0:s}", now);
string.Format("Custom date:        {0:ddd, MMM dd, yyyy}", now);

Using format specifiers, you could also load the string format expression from an embedded resource file using the ResourceManager[^]. While the current CultureInfo is used to format the actual dates, times, and numbers (or a specific culture's DateTimeFormatInfo or NumberFormatInfo is passed as the IFormatProvider parameter), you could use this approach to load localized strings - including strings that use a right-to-left reading order.

For instance, if you want to format a date using another culture's formatting information without changing the executing thread's CurrentCulture, you could simply get the DateTimeFormatInfo for that culture:

CultureInfo culture = new CultureInfo("de-DE");
DateTime dt = DateTime.Now;
string.Format(culture.DateTimeFormat, "Großdatum:          {0:D}", now);

The date would use the German culture information for dates and times to format the value, including localized day and month names and the order of the various elements of the string.

To further extend this as mentioned previously, you could get the string format expression from an embedded resource. For simplicity, the Thread.CurrentUICulture is assumed to be set appropriately.

ResourceManager resources = new ResourceManager("Strings.resources",
  GetType().Assembly);
string format = resources.GetString("CommonFormat");
DateTime dt = DateTime.Now;
string.Format(format, now);

The value keyed as "CommonFormat" would contain the string format expression, like "Long date: {0:D}". These are commonly stored in ResX files, which is beyond the scope of this article.

Extending Existing Types with Custom Format Providers

Numeric types and the DateTime structure already have associated format providers that provide a lot of different formats. The DateTimeFormatInfo even lets you use custom format specifiers. But what if you want to format any type and either can't extend the type or don't want to just for the same of formatting?

You can implement the ICustomFormatter interface and expose the implementation through a custom IFormatProvider, which you can pass as the first parameter to methods like String.Format. Two custom format providers are included with the sample project for this article: a custom NumberFormatInfo class which can convert numbers to any radix (from the .NET Framework SDK, included because its very handy) and to Roman numerals up to 3,999,999; and a StringFormatInfo class which can convert strings to Morse Code (includes support for current characters, including "@" which was just added early in 2004). In both samples, both the ICustomFormatter and IFormatProvider interfaces are implemented to make things easy, and there's absolutely no reason why you couldn't do the same in production code. The declaration of the StringFormatInfo class follows.

public sealed class StringFormatInfo : IFormatProvider, ICustomFormatter
{
}

As you can see, the class implements IFormatProvider so that we can simply pass it into call like String.Format:

StringFormatInfo fmtinfo = new StringFormatInfo();
string.Format(fmtinfo, @"The morse code for ""{0}"":\n{0:m}", "Hello, world");

IFormatProvider[^] declares a single method, GetFormat(Type)[^]. To implement this method, see if the argument is of type ICustomFormatter, not your class type. This has to do with the internal implementation of formatting methods.

public object GetFormat(Type formatType)
{
  if (typeof(ICustomFormatter).Equals(formatType)) return this;
  return null;
}

ICustomFormatter[^] also declares a single method, Format(String, Object, IFormatProvider)[^]. This is where the real work is done. You should first make sure that a object to be formatted is not null (Nothing in Visual Basic), unless you want to handle null values specially with your custom format provider. Then check the format string - the first parameter declared in the method. You can choose whether or not to compare the strings in a case-sensitive manner. The DateTimeFormatInfo provided in the FCL, for example, differentiates between "d" and "D", and several other characters. For the StringFormatInfo example, a case-insensitive comparison is performed.

public string Format(string format, object arg, IFormatProvider formatProvider)
{
  if (arg == null) throw new ArgumentNullException("arg");
 
  if (format != null && arg is string)
  {
    string s = format.Trim().ToLower();
    if (s.StartsWith("m"))
      return FormatMorseCode(arg as string, format);
  }
 
  if (arg is IFormattable)
    return ((IFormattable)arg).ToString(format, formatProvider);
  else return arg.ToString();
}

In this example, I simply trim the format and convert it to lower case. I could've just as easily used String.Compare, but if you supported multiple format specifiers you don't want the overhead of a case-insensitive string comparison in each condition. After that, I check if the format specifiers starts with an "m". If so, I pass certain arguments to my FormatMorseCode method, for which you can see the implementation in the sample project.

One important thing to remember is that if you don't handle the object type or the format specifier is not valid for your IFormatProvider implementation, you should handle the object appropriately by determining if it supports the IFormattable interface (which I'll cover shortly); if it does, call the IFormattable.ToString(String, IFormatProvider) method; otherwise, just call ToString which is declared by the Object class and is thus inherited by every class, some of which override the default implementation which simply prints the fully-qualified type of the object.

Format providers may also define properties that control the formatting in specific ways. The DateTimeFormatInfo, for example, defines many properties to get and set the Calendar to use, the localized names of days, and much more. The sample StringFormatInfo provides a LineWidth property which helps ensure that dots and dashes are kept together for for clarity. If you were to separate your IFormatProvider and ICustomFormatter implementations, you should typically define these properties on your IFormatProvider implementation since it is passed to the ICustomFormatter implementation, as well as the IFormattable implementation which I'll discuss shortly. In this case, make sure you are dealing with the right type and get the properties you need by casting to your type. When possible, exposing these properties as format specifier options may also be advantageous, though you probably shouldn't make it too complicated and simply expose what would be easy to represent and parse.

Custom format providers give you the ability to use a simple class or classes that provide custom format specifiers and options to pre-existing types. This is quite a bit of unnecessary work, though, when you define your own types.

Extending Your Types with Custom Formatting

When defining your own class and structure types, you can provide custom formatting easily enough through any means you want. If you want to support the standard formatting routines in .NET, however, you must implement IFormattable. You could also simply decide to override the ToString() method inherited from the Object class if you want to display a custom string for a type and don't need to support more than one type. Point is one example with only overrides ToString() and returns a string in the form of "{X=X, Y=Y}".

IFormattable[^] declares just one method again, ToString(String, IFormatProvider)[^]. There again is the IFormatProvider interface, which is another good reason to define format properties on your IFormatProvider implementation. You could always check for different types you support, then cast them and get the properties you need to help control the format of your output. In this simple example, I do just that:

char c = divisor;
// ...
if (formatProvider is NumberFormatInfo)
  if (((NumberFormatInfo)formatProvider).UseDiacritic)
    c = diacritic;

Implementing IFormattable.ToString is pretty easy, but you should also consider overloading ToString to provide a single parameter for each of type. These can simply call the implementation method, which uses the format specifier and optionally information from the IFormatProvider argument to format your type as a string.

public override string ToString()
{
  return ToString("g", null); // Always support "g" as default format.
}
 
public string ToString(string format)
{
  return ToString(format, null);
}
 
public string ToString(IFormatProvider formatProvider)
{
  return ToString(null, formatProvider);
}
 
public string ToString(string format, IFormatProvider formatProvider)
{
  if (format == null) format = "g"; // Set default format, which is always "g".
  // Continue formatting by checking format specifiers and options.
}

See the Rational struct example - a mostly full-featured fractional number structure - for more details of how IFormattable.ToString is implemented in the sample project. It isn't much different from examples above for custom format providers for existing types.

Summary

Formatting strings using the .NET Framework is a very flexible system when you implement the right interfaces. You can provide both custom formatters for existing types and implement custom formatting in your own types. The sample project for this article demonstrates both way of providing custom formatting and includes a simple test application to try out the different combinations.

Using custom format providers and formatters can help streamline your code so that you can load custom format expressions from different sources like databases or resource files, and provide custom formatting options for indexed arguments, even reusing the same arguments throughout the format expression. With a little extra code, you should have no problem providing just about any string format when your application requires it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Heath Stewart
Program Manager Microsoft
United States United States
Heath Stewart is a happily married software engineer originally from the Midwest and a graduate of Iowa State University. Heath start programming early in life and enjoys continuous research and development in new languages, frameworks, and platforms. Fluent in many different programming languages, he has developed many large-scale software solutions for companies in different areas, such as Internet filtering, intrusion detection systems, production management systems, and web applications for various purposes. He also enjoys photography.
 
Currently, Heath is a Program Manager in the Visual Studio Professional Deployment Experience (VSPro DEX) team at Microsoft. Previous to his employment, he was a Microsoft MVP for Visual C#.
 
He is also a CodeProject protector and is happy to help the development community.
Follow on   Twitter

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralSimilar Article - "CustomFormat"memberScott Rippey22-Oct-09 7:36 
I have submitted an article with code that performs similar functionality to yours. Custom Formatting in .NET - Enhancing String.Format to all new levels![^]
It has a plugin system that allows custom formatting like yours, it has Conditional Formatting, and it allows syntax such as:
result = CustomFormat("{Name} is {Age} years old", person)
Take a look if you are interested, and let me know if there are areas for improvement!
GeneralMy vote of 1memberiusdghfio eilqsbhao;jfhfo;dhag;jkd2-Feb-09 6:31 
Copy & Paste from MSDN. Article has no value.
Generalurgent....need help formatting stringmemberslayer_stb6-Nov-07 17:49 
i have a column which have value as string such as 06563010212138000129.
when i try to display to a datagrid it become : 6,563010212138E+18
i want to display it with its original value...
how to do it ??
note, this record from a csv file....
 
please help me...

QuestionCustom FormattingmemberSheraz Cheema7-Mar-07 20:49 
Hi
 
I want to write some data on file like given below
sw.WriteLine("{0,15}{1,15}",BlockName, ApplicantName);
 
If BlockName exceed 15 characters it will write complete BlockName and the ApplicantName. How I can force my formatting so that maximum 15 characters of BlockName written on file.
 
Sheraz
QuestionCan formatters be "registered"?memberChuck_Esterbrook3-Mar-07 17:17 
Thanks for the article. I see this snippet of code:
 
StringFormatInfo fmtinfo = new StringFormatInfo();
string.Format(fmtinfo, @"The morse code for ""{0}"":\n{0:m}", "Hello, world");

 
I'm wondering if there is a way (in .NET 2.0) to get the StringFormatInfo() instance to somehow be "registered" with the runtime such that the "m" code can be used in any formatting string without requiring an additional argument.
 
For example, .NET already supports "n", "d", "g" and others without having to pass in a format info instance everywhere.
 
Is there a call, or set of calls, that would add "m" to that list so one could simply write:
 
string.Format("The code is: {0:m}", "Hello");
 
?

AnswerRe: Can formatters be "registered"?protectorHeath Stewart4-Mar-07 19:20 
Not that I know of. You could create a container, which would contain the string. Implement IFormattable on your container type. You could even implement an implicit cast operator to/from string to make it easier to use.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]

GeneralHelpful, but I need moremembermasonblake16-Oct-06 13:27 
This article was great, and I got things to work just fine. However, I'd really like to use my new format provider in the DataFormatString expression on the GridView of ASP.NET 2.0. How can I make this available to the GridView? I could use template columns and programatically use my format, but I'd like to call it directly within a BoundField column in the GridView.
 
Possible? Any help left on this article?
GeneralRe: Helpful, but I need moreprotectorHeath Stewart16-Oct-06 13:40 
In your template just set the DataFormatString. The documentation for the BoundField.DataFormatString property even has an example. I'm not sure what you mean by "but I'd like to call it directly...".
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]

GeneralRe: Helpful, but I need moremembermasonblake16-Oct-06 13:53 
Thanks for responding so quickly, Heath. What I mean by "calling it directly" was that just that I want to be able to use the DataFormatString="" attribute on the BoundField for my GridView. I've implemented my IFormatProvider class and I get the correct results when using it through the code like so:
 
ret = String.Format(new PropperString(), "{0:PS}", DataBinder.Eval(objRow, "BandName").ToString());
 
But when I try to use my new PropperString ("PS") format in the GridView DataFormatString attribute, it doesn't work.
 
DataFormatString="{0:PS}"
 
Also, I have this class in a separate project than the ASP.NET web app I'm calling it from. I do have a reference in my project, will that make a difference in attempting the DataFormatString attribute usage?
 
Mason
GeneralRe: Helpful, but I need moreprotectorHeath Stewart17-Oct-06 4:30 
The article here isn't applicable. The inner workings of the DataBoundControl and its child classes is different than what is described here. If you want to format an object completely different, derive a new class from BoundField and override FormatDataValue. This method is actually defined in the base class DataControlField, so you can derive your class from that as well but you should find a class that functions as closely as you want to avoid having to override and provide more functionality on your own.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]

QuestionUsing this in ToString methods as well as in String.Format ?memberGabriel Underwood8-Sep-06 14:45 
I am having a great deal of difficulty in using the ICustomFormatter / IFormatProvider pattern when formatting a long using the Long.ToString() method.
 
Can Heath or anyone explain how to use a custom formatter in this scenario?
 
When I execute the code below using Heath's Samples.NumberFormatInfo class, I cannot get roman numeral formatting when I call the overloaded Long.ToString() methods passing the custom formatter.
 
Samples.NumberFormatInfo info = new Samples.NumberFormatInfo();
long someLong = 25;
 
// behaves as expected as per the article
String test1 = string.Format( info, "{0}: {0:rn}", someLong);
Console.WriteLine(test1);
 
// doesn't invoke the Samples.NumberFormatInfo Format method
String test2 = someLong.ToString("0:rn", info);
Console.WriteLine(test2);
 
// doesn't invoke the Samples.NumberFormatInfo Format method
String test3 = someLong.ToString(info);
Console.WriteLine(test3);
 

Normally I would be able to work around this by just calling String.Format, butI am trying to register my own custom IFormatProvider to a Windows Forms DataGridViewCellStyle object, which I believe ends up calling .ToString(string,IFormatProvider) internally when formatting data.
 
Is this a known bug or am I missing something?
AnswerRe: Using this in ToString methods as well as in String.Format ?protectorHeath Stewart8-Sep-06 15:35 
I recommend for the sake of learning to use either ildasm.exe from the .NET Framework SDK or search for ".NET Reflector" (or another decompiler) and look at the implementation for Int64.ToString(). It might not call into IFormatProvider implementations.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]

GeneralRe: Using this in ToString methods as well as in String.Format ? [modified]memberGabriel Underwood11-Sep-06 8:55 
Hi Heath -
 
The documentation for ICustomFormatter reads:
"For example, use this interface to provide custom formatting of the value of an object by the String.Format or Int32.ToString methods."
 
It's pretty clear there is a bug in the System.Int32, System.Int64, and System.Double implementations of IFormattable.ToString(string format, IFormatProvider formatProvider), which will *not* use a custom IFormatProvider implementing ICustomFormatter if provided.
 
I've looked at the types in question in Lutz Roeder's .NET Reflector.
Looking at the implementation in .NET Framework 2.0, I see:
 
System.String.Format(IFormatProvider provider, string format, params object[] args)
 
- does some error checking and creates a StringBuilder and calls StringBuilder.AppendFormat, which when decompiled shows:
 

System.Text.StringBuilder.AppendFormat(IFormatProvider provider, string format, params object[] args)
{
.....
if (provider != null)
{
formatter1 = (ICustomFormatter) provider.GetFormat(typeof(ICustomFormatter));
}
 
....
if (formatter1 != null)
{
// Great, calls the supplied ICustomFormatter.Format method
text2 = formatter1.Format(text1, obj1, provider);
}
 
... otherwise cast obj1 to IFormattable and call IFormattable.ToString
}

 
So that looks good which is why String.Format works.
 
However, looking at System.Int64.ToString(string format, IFormatProvider provider) reveals it never tries to get an ICustomFormatter, no matter what format provider is passed in or what the format string is.
 

public string ToString(string format, IFormatProvider provider)
{
return Number.FormatInt64(this, format, NumberFormatInfo.GetInstance(provider));
}

 
Unfortunately, Number.FormatInt64 takes a (sealed) NumberFormatInfo and not an IFormatProvider, and NumberFormatInfo.GetInstance(IFormatProvider formatProvider) always returns a NumberFormatInfo and not an IFormatProvider.
 
NumberFormatInfo.GetInstance does call into the supplied formatProvider's GetFormatmethod, but passes in typeof(NumberFormatInfo) never typeof(ICustomFormatter).
 

I believe that the Int64.ToString(string format, IFormatProvider formatProvider) call sequence needs to at some point call and test the results of formatProvider.GetFormat(typeof(ICustomFormatter)) to allow custom formatting as per the documentation of System.IFormatProvider.GetFormat in the .NET Framework SDK Class Library Reference.
 

 

I believe this is a bug in Int64.ToString(string format, IFormatProvider formatProvider) and similarly in Double.ToString(string format, IFormatProvider formatProvider) in their implementations of IFormattable.ToString(string format, IFormatProvider formatProvider) .
 

There doesn't appear to be a workaround for this bug when trying to provide a custom IFormatProvider in a System.Windows.Forms.DataGridView.DataGridViewCellStyles.
 
When looking at the decompiled System.Windows.Forms.DataGridViewCell class, this uses an internal implementation class called System.Windows.Forms.Formatter, this class has a FormatObjectInternal method defined as:
 
private static object FormatObjectInternal(object value, Type targetType, TypeConverter sourceConverter, TypeConverter targetConverter, string formatString, IFormatProvider formatInfo, object formattedNullValue)
{
...
 
if (((targetType == Formatter.stringType) && (value is IFormattable)) && !string.IsNullOrEmpty(formatString))
{
// This calls the broken Int64.ToString method
return (value as IFormattable).ToString(formatString, formatInfo);
}
 
... then check if there's a TypeConverter, etc. etc
}

 

It appears that there is no way to use a custom IFormatProvider/ICustomFormatter implementation to do custom formatting in a DataGridView, by setting the DataGridViewCellStyles.Format and FormatProvider properties.
 
Update: There is a workaround for using this in the DataGridView, by implementing the DataGridView.CellFormatting event.
 
void mGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
 
{
 
DataGridViewColumn col = mGrid.Columns[e.ColumnIndex];
 
if (col.DefaultCellStyle.FormatProvider is ICustomFormatter)
 
{
 
Object originalData = e.Value;
 
e.Value = String.Format(col.DefaultCellStyle.FormatProvider, col.DefaultCellStyle.Format, originalData);
 
}
 
}
 
This works by calling String.Format rather than letting the Windows Forms formatting infrastructure call the broken IFormattable.ToString method for the native numeric types.
 
- Gabriel
 


 

 

 
-- modified at 17:39 Monday 11th September, 2006
GeneralRe: Using this in ToString methods as well as in String.Format ?protectorHeath Stewart11-Sep-06 14:57 
Excellent analysis! It appears to be a bug, yes. I recommend going to Windows Connect[^] and filing a bug against the runtime. They may have a specific reason for it, too.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]

GeneralRe: Using this in ToString methods as well as in String.Format ?memberGabriel Underwood12-Sep-06 7:57 
Thanks Heath for pointing me to Windows Connect.
I raised bug 201155, we'll see what they say.
- g

GeneralI am not getting it properlymemberMaksud Belim15-Jun-06 12:29 
I have read the FAQs and your suggession. But, I did not get it properly
 
Maybe I haven't capablity to read it OR
You have not presented it Deeply.
 
Whatever pl. make it easy to understand and with more examples.
 


 
MKBELIM
Questionany other benefit of string format?memberiambug200320-Oct-05 21:44 
Dim myName As String = "ALEX"
Dim myFinalText As String
 
myFinalText = String.Format("My name is : {0}", myName)
myFinalText = "My name is : " + myName
 
in your article you mention about "I'm sure you'll agree that the string format expression is much easier to read and change at runtime."
 
So i would like to know is there other benefit using the string format like the performance issue, is that any different?
 
Thank youSmile | :)
AnswerRe: any other benefit of string format?protectorHeath Stewart21-Oct-05 6:43 
For something like that I wouldn't recommend string formatting in every case. If you were going to localize your application, however, string formatting lets you keep the same parameter list but localize and store the format. My article discusses this as a main point, comparing it with what developers have to do in C/C++ and Perl.
 
Words don't always go in the same order in different languages and simple string concatenation will make localizing your application very difficult.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]
GeneralRe: any other benefit of string format?memberiambug200325-Oct-05 22:36 
thank you alot for you advise
GeneralRe: any other benefit of string format?memberramadu9-May-06 16:53 
String concatenation causes various performance issues as new immutable strings are created every time concatenation is done. Are these issues mitigated when String.Format is used?
 
i.e. does doing string.Format("My name is: {0} {1}", FirstName, LastName) create three strings and concatenate the variables to the original string?
 
Sriram Venkataramani
GeneralEfficiency QuestionmemberSteve Schaneville9-Oct-05 18:06 
Great article, appreciate you sharing your knowledge. OK, my question... about 3/4 the way down in your article you have this code:
 
...
string s = format.Trim().ToLower();
if (s.StartsWith("m"))
...

 
And then just below that you have this statement: "I could've just as easily used String.Compare, but if you supported multiple format specifiers you don't want the overhead of a case-insensitive string comparison in each condition."
 
I thought that a String.Compare with case-insensitivity was much faster than a ".ToLower()" call on a string (which creates a new string), and was created for the very purpose of avoiding such slow (memory allocating) calls.
 
Am I wrong? Should I be calling ToLower/ToUpper instead of String.Compare with case-insensitivity?
 
~Steve
AnswerRe: Efficiency QuestionprotectorHeath Stewart9-Oct-05 18:39 
If you look at the IL for String.StartsWith it does call String.Compare. Case-insensitive comparisons are typically as costly as making a string all lower- or upper-case. My comment about doing this once vs. calling String.Compare in a case-insensitive manner for every switch your formatter may support is true.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Customer Product-lifecycle Experience
Microsoft
 
[My Articles] [My Blog]
GeneralRe: Efficiency QuestionmemberSteve Schaneville10-Oct-05 16:28 
Ok, thanks! Just wanted to know, in case I was doing it the slow way in my own code... Wink | ;)
 
~Steve

Generalsample questionsussAnonymous4-Feb-05 8:33 
I have to provide an output for 3416 as 3.4K
If I need 3K it's easy: "0,K". What should I use in my case?
I'd prefer not to change the number itself dividing by 1000, just use the proper format.
Thank you!
zenit

GeneralRe: sample questionprotectorHeath Stewart4-Feb-05 8:35 
Then you write an IFormatter that provides number formatting that devides the number for you. That's exactly what this article covers.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
 
[My Articles] [My Blog]
GeneralRe: sample questionsussAnonymous4-Feb-05 10:49 
Thank you for your quick reply.
Actually me case is more complicated:
I have a method to define the format string:
public string GetFormat(double n) {
if (n > 999999999) return "0,,,G";
if (n > 999999) return "0,,M";
if (n > 999) return "0,K";
return String.Empty;
}
then I provide the output of the method to the Dundas software LabelStyle.Format property,
so I need a string. It worked fine but now I need one digit "after a decimal point", like 2.5G or 75.8K (it calculate bytes).
Is it possible to get a string that will convert 3416 to 3.4K ?
 
Thank you,
zenit
GeneralRe: sample questionprotectorHeath Stewart4-Feb-05 13:06 
The default format provider for numeric types can already do that. For example:
Console.WriteLine("{0:f1}M", 3416f / 1000);
This is documented for the NumberFormatInfo class, as well as Single.ToString.
 
Be sure to use 3416f, otherwise you'll always get 3.0M. The reason is because you're dividing two integers. You need to use at least one float (System.Single) otherwise it will be rounded.
 
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Software Design Engineer
Developer Division Sustained Engineering
Microsoft
 
[My Articles] [My Blog]
GeneralMy humble opinionmemberjmw20-Jun-04 16:20 
I've never really liked string formatting like this... it all seems rather archaic and orthogonal to the C# language. I mean, whey specify the formatting in a weird cryptic code hidden in the string when the formatting could be defined by the parameters to the ToString method in the parameters, and then just inserted like any other string.
 
Substitution is useful. {0} is useful. But anything else just seems like double the maintenance, unintuitive and damn-well mysterious! It almost certainly involves a trip to the documentation which I really dislike since intellisense came along.
 
That's just my opinion. Smile | :)
 

GeneralRe: My humble opinionprotectorHeath Stewart20-Jun-04 17:50 
jmw wrote:
It almost certainly involves a trip to the documentation which I really dislike since intellisense came along.
 
You're entitled to your opinion, but if you hesitate to read the documentation, then you're shooting yourself in the foot and merely just writing code - something anyone can do.
 
Understanding takes research, and just relying on IntelliSense is not development - it's just writing code.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: My humble opinionmemberjmw21-Jun-04 2:40 
No sorry, I disagree with that.
 
Intellisense *is* documentation. It's documentation in a much better place, displayed in precisely the context where it is needed most.
 
And I don't understand why you imply that intellisense somehow detracts from understanding. It doesn't, it improves understanding because the information that you need to carry out the operation you are coding is right there in front of you, without the need to waste time looking up things on the web.
 
It's basically a matter of productivity.
 
The ToString() method is type safe, syntax checked, compiled code that provides intellisense and is thus far more readable and tells you exactly what it will do.
 
The format strings method is not type safe, is not syntax checked (until runtime in some cases, in other cases not at all), interpreted code, that for most people requires a trip to documentation to figure out what it is doing.
 
You see why I just don't get it?

GeneralRe: My humble opinionprotectorHeath Stewart21-Jun-04 2:48 
No, it is not documentation. Documentation tells you all the information about a method, property, class, etc. Looking at the documetnation will also show you alternatives. And by reading the documentation (such as the class library documentation), you also become familiar with what's available rather than spend your time doing what the BCL has already provided. I spend a lot of time in the C# forum and see this all the time.
 
IntelliSense gives you summary information, as well as parameter information when you're currently filling a parameter - no remarks, no examples, no "See also", nothing.
 
ToString is used by the String.Format (and similar methods). If the type implements IFormattable, then it's implementation of ToString(string, IFormatProvider) is used. The same format specifiers for that ToString (or overloads, which typically call the IFormattable implementation) are the same as those for a formatting string. Take a little time and study how these methods work using ildasm.exe or something before you start knocking the differences. When it gets right down to it, there really are no differences becuase the same implementation is being used. The only real difference with using format specifiers in the format string is that they are parsed out and then passed to an IFormattable.ToString implementation.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: My humble opinionmemberjmw21-Jun-04 2:57 
I think most would agree that documentation isn't black or white, there are varying levels of documentation, intellisense providing one, help files providing another and books providing yet another. They're each useful.
 
For many who are familiar with the functioning of a class, intellisense is sufficient. Intellisense provides you with the list of options available for formatting a particular instance. It's the integration with the IDE that makes it most productive and attractive to the developer.
 
I know ToString may ultimately be used by Format -- I'm not talking about implementation, I'm talking about the interface to the user, and its integration with the compiler.
 
It's a fairly basic fact that compiled code runs faster, is subject to syntax and type checks and is just generally a better alternative to untyped interpreted code. In my mind, format strings are interpreted and don't go through any of these checks until runtime (if at all). (unless you're telling me that the compiler breaks down strings to look for things like that at compile time and optimizes accordingly? I doubt that, but I'm open).
 
When I have my developers write code, I want it to be as readable as possible. It's difficult enough remembering cryptic format string specifiers (just remembering those sscanf days), let alone when custom specifiers are introduced.
 
Code that calls functions with parameters is far easier to read and all-around produces more reliable code.
 
John
GeneralRe: My humble opinionprotectorHeath Stewart21-Jun-04 3:10 
jmw wrote:
For many who are familiar with the functioning of a class, intellisense is sufficient.
 
Funny - I understand and know it well enough to not need an IDE and still program faster and better than most people.
 
The point is that most people don't know this environment at all. Hang out in the forums for a while and see the kinds of questions we get. "What's method X do?" "Why won't this work?" (no specifics given). Etc., etc.
 
jmw wrote:
It's difficult enough remembering cryptic format string specifiers (just remembering those sscanf days), let alone when custom specifiers are introduced.
 
I thought you said you're familiar with the Framework. I have no problem remember these, and if you expect to understand all the code in an entire application code base as you're scanning through, that's ludicrous. Sure, you can understand why a method uses the other classes in the GCL, but how all those types in your application relate to each other is another matter, unless you're an expert in your entire application's code base.
 
Argue about what you think documentation is all you want, but IntelliSense uses nothing more than tooltips and books are seldomly comprehensive like documentation (they're meant to teach concepts, unless you get the printed reference for the BCL and other similar books, of course).
 
jmw wrote:
Code that calls functions with parameters is far easier to read and all-around produces more reliable code.
 
That's a matter of opinion. I'd rather see a concise format string than a variable param list littered with "ToString". When developed correctly, the code is just as reliable as using the same format specifiers in ToString. Perhaps it's the developers who aren't reliable if they're getting it wrong.
 
Besides, as I already pointed out this makes localization much easier. Sure, you could achieve the same with indexers (minus the format specifiers) but what if you want to localize the format specifiers as well? You'd have to include localization code in each call to ToString, where I could localize the format string and be done with it.
 
If you don't like doing it this way, fine. It's your code.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: My humble opinionmemberjmw21-Jun-04 3:27 
Heath Stewart wrote:
When developed correctly, the code is just as reliable as using the same format specifiers in ToString. Perhaps it's the developers who aren't reliable if they're getting it wrong.
 
That's my whole point. Format strings are more prone to error and mistakes because of lack for support in the IDE and by the compiler. Any errors you make in the syntax of a ToString call will be picked up during compilation.
 
I'm sorry but to say 'perhaps it's the developers who aren't reliable' is so arrogant. All developers make mistakes, unless you're telling me you've never got a compilation error in your life?
 
Heath Stewart wrote:
Besides, as I already pointed out this makes localization much easier.
 
I don't see how, implementations of ToXXXString methods should be culture aware anyway.

GeneralRe: My humble opinionprotectorHeath Stewart21-Jun-04 3:33 
jmw wrote:
I'm sorry but to say 'perhaps it's the developers who aren't reliable' is so arrogant. All developers make mistakes, unless you're telling me you've never got a compilation error in your life?
 
Of course I've made mistakes - as you say, everone does. But see, there's a little thing me and other people called "testing", where problems like this come out.
 
jmw wrote:
I don't see how, implementations of ToXXXString methods should be culture aware anyway.
 
Most are, but do you expect there to be defined a ToXXXString for every format specifier available? Just take a look at the DateTime struct. It has a few such methods but many more specifiers. Besides, the interfaces and implementations of IFormattable, IFormatProvider, and ICustomFormatter all work together to provide an abstract programming model. I suppose you don't like those, either?
 
Besides, why don't you use the IL Disassembler to take a look at those ToXXXIString implementations. I think you'll find most (if not all in the BCL) simply call the IFormattable.ToString implementation. At least that's what I've seen in the 1.x BCL assemblies and do in our product, as well as having seen that in other products.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: My humble opinionmemberjmw21-Jun-04 3:46 
Heath Stewart wrote:
Most are, but do you expect there to be defined a ToXXXString for every format specifier available? Just take a look at the DateTime struct.
 
Actually the DateTime struct is a bad example as I think they've done a poor job of exposing the formatting functions. An overloaded ToString function that has separate parameters to indicate the various elements of the resultant string would be preferable. I certainly think that should be the way any custom formatting functions should be defined.
 
Heath Stewart wrote:
Besides, the interfaces and implementations of IFormattable, IFormatProvider, and ICustomFormatter all work together to provide an abstract programming model. I suppose you don't like those, either?
 
Er and why do you suppose that. I don't see what abstract programming models have to do with this. Other than the fact there's nothing abstract about a formatting string whatsoever. I do love high level, object oriented designs and APIs, and perhaps that's why I don't like format strings.
 
Don't get me wrong -- I'm a great believer in the right tool for the right job, and I believe people should use different, domain specific languages where it's more suitable and helps productivity.
 
Take for example regular expressions. They suffer from the same problem of having no compiler support, yet regular expressions certainly do cut down on the verbosity of the equivalent C# code.
 
This is really an IDE issue, and isn't an argument for one use over another. If the IDE understood sub-languages in C# code, and could provide various compile time checks, then this wouldn't be so much of an issue, and maybe it could even provide drop-down intellisense when you insert that '{' in the string.
 
In the meantime, I'm going to stick to using C#.
 
John

GeneralRe: My humble opinionprotectorHeath Stewart21-Jun-04 4:34 
jmw wrote:
Er and why do you suppose that. I don't see what abstract programming models have to do with this. Other than the fact there's nothing abstract about a formatting string whatsoever. I do love high level, object oriented designs and APIs, and perhaps that's why I don't like format strings.
 
What do you think this article was about? Without the support for the interfaces I mentioned, none of this would be possible. String.Format, StringBuilder.AppendFormat, TextWriter.WriteLine, etc. would never work. There is an abstract model here using the interfaces like IFormatProviders and the like, without which localization wouldn't be possible in .NET, since that's what the string formatting methods use to output localized (for a region, not necessarily a culture which is the job of other interfaces in System.Globalization and System.Resources) text. You might try reading about these various interfaces to see how they all work together, and even examine a few things like those various ToString methods (and DateTime's not the only struct that uses what I mentioned) and see how they work.
 
jmw wrote:
This is really an IDE issue, and isn't an argument for one use over another. If the IDE understood sub-languages in C# code, and could provide various compile time checks, then this wouldn't be so much of an issue, and maybe it could even provide drop-down intellisense when you insert that '{' in the string.
 
Perhaps, but that information isn't provided by the CLI implementation. It's a documentation feature only. A new documentation syntax would need to be defined, not that that's impossible (I'm on the NDoc[^] team and we're currently working with Microsoft on that for a standard platform support syntax).
 
There nothing that says a format string doesn't belong in an object-oriented environment. String I/O is one of the most basic attributes of computer programming, whether it's procedural or object-oriented. It sure beats printf et. al. as I mentioned in my articles, and is still very flexible. If you don't like reading the documentation in order to leverage this powerful feature, that's your prerogative.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: My humble opinionmemberjmw21-Jun-04 4:47 
Heath Stewart wrote:
What do you think this article was about? Without the support for the interfaces I mentioned, none of this would be possible.
 
Yes but that's implementation! I'm not talking about implementation! I'm talking about how the developer *uses* the features, and how the features integrate with the environment. I'm sure the implementation model is just wonderful.
 
Heath Stewart wrote:
Perhaps, but that information isn't provided by the CLI implementation.
 
I haven't thought about it that much, but I'm thinking it's possible to achieve some degree of support in the CLI through attributes on methods.
 
In a sense format specifiers are parameterized instantiation commands, just written in the 'format string' language. So in that sense, format strings conform to a declarative language of sorts, and looking at XAML and BAML it's quite clear that declarative languages are fully supportable by the CLI.
 
So I think there *is* room for better integration with languages such as format strings (and regex) with the IDE. It's probably just a matter of time.
 
And, like I said, once there's an IDE for the language, it'll be more appealing to me, and to others.
 
John

GeneralRe: My humble opinionmemberSameer Alibhai6-Jun-07 3:03 
I disagree, it makes it MUCH more readable, easier to add new parameters, etc..
It is a tad bit slower, but in most cases who cares!
 
Please see my post - Use String.Format (C#) instead of Chopping Strings[^]
 
Author, SharpDeveloper.NET
 

GeneralGreat article!memberJeff Varszegi29-Apr-04 5:41 
One minor correction: Perl is not an acronym. Got my 5.
 
Regards,
 
Jeff Varszegi
 
EEEP!
GeneralRe: Great article!editorHeath Stewart29-Apr-04 6:13 
Interesting. I also just figured that if the man pages say so, it must be true! Smile | :) I do know that using "PERL" is incorrect, but I figured it was just because it is common enough to use "Perl" instead. I will update my article.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: Great article!memberJeff Varszegi29-Apr-04 6:32 
It wasn't really a nitpick, I just stumbled across that page the other day and found it interesting. I'd never even heard of "Pathologically Eclectic Rubbish Lister", but thought it was kinda funny.
 
Regards,
 
Jeff Varszegi
 
EEEP!  An Extensible Expression Evaluation Package
GeneralInternationalizationmemberTom Clement15-Apr-04 7:41 
Hi Heath,
Great article. One point I'd add is the importance of indexed format specifiers to creating internationalized code. This is because the grammar of various languages may make it important to reverse the order of sentence components. Since the original printf() format specification didn't support indexers, internationalization was not really possible. The nice thing about string.Format() is that you are forced (in a nice and natural way) to reference the arguments by index, so internationalization becomes just a matter of putting the strings into resources and localization becomes relatively simple.
 
Tom Clement
Apptero, Inc.
GeneralRe: InternationalizationeditorHeath Stewart22-Apr-04 9:42 
A little off-topic, but since you seem to be no stranger to localization, .NET 2.0 features the ComponentResourceManager which makes localization of a project much easier.
 
I was just browsing through some stuff today in VS.NET 2005 on my other partition and noticed that my ResX file (when testing some localization stuff) wasn't nearly as big. In .NET 1.x, we never localized our apps because anything with LocalizableAttribute set to true incurs additional calls to get information from the embedded resource. This was not acceptable.
 
.NET 2.0 introduces and uses the ComponentResourceManager which enumerates the resources, caches them for the object's lifetime, and matches up properties based on a root name for a control and assigns them. This performs much better than using a ResourceManager which must know names up front, and allows developers to localize an application or library without littering code with ResourceManager.GetObject calls for every localizable property!
 
I'm currently planning a write-up on localization and plan on mentioning some of this. I know it's early, but I think this is a cool feature and have yet to see any MSDN articles on that (they're all about C# language enchancements, generics, and Indigo lately it seems).
 
Just thought you might like to know. Smile | :)
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: InternationalizationmemberTom Clement22-Apr-04 10:56 
Thanks Heath,
 
I haven't been able to keep up with what's going on with .NET 2.0, so it's great to hear about the ComponentResourceManager. Thanks for the summary! I look forward to your write-up. Smile | :)
 
Tom Clement
Apptero, Inc.
My articles[^]
GeneralRe: InternationalizationmemberNelak29-Nov-04 9:10 
Do you happen to know how to handle collection localization using ComponentResourceManager.
ApplyResources doesn't seem to cover localizing item collections.
QuestionWhat about @?memberjupiter_genie30-Mar-04 15:10 
Hello Buds,
 
As you know the use of @-quoting is to simplify string implementation but it also prevents the compiler from processing the escape sequences. When I started my project and tried to retrieved a string object from a resource, the string I got was @-quoting typed by use method ResourceManager.GetString().
 
e.g:
In my resource: "Hi See Sharp!\nHow ya doin'?"
Loaded string : @"Hi See Sharp!\nHow ya doin'?"
 
Then you get the MessageBox showed up, and as the result you still have the "\n" on the screen. It's a real painD'Oh! | :doh: .
 
Does anyone out there know how to convert an @-quoted string back to normal literal string? Confused | :confused:
 
Thanks.
TuanNA
 
Nguyen Anh Tuan
AnswerRe: What about @?editorHeath Stewart30-Mar-04 16:38 
A @-quoted string is a literal. To put line-breaks into it, you actually put a line break in the string like:
string s = @"This sentence
is broken.";
If you see this in the debugger windows, don't worry about it. The information you see there is typically encoded wrong and will be improved in VS.NET 2005.
 
The fact of the matter is that "@" is only a compile-time directive, so any strings you get programmatically at runtime won't be affected.
 
I just tried it myself with no problems:
// Test.cs
using System;
using System.Resources;
 
public class Test
{
  static void Main()
  {
    ResourceManager resources = new ResourceManager(typeof(Test));
    Console.WriteLine(resources.GetString("Test"));
    System.Windows.Forms.MessageBox.Show(resources.GetString("Test"));
  }
}
 
// Test.txt
Test = This is\na test.
 
Build:
resgen Test.txt Test.resources
csc /t:exe /res:Test.resources Test.cs
Got breaks in both the console and the message box.
 
 
Microsoft MVP, Visual C#
My Articles
GeneralRe: What about @?memberjupiter_genie30-Mar-04 20:39 
Dear Steward,
 
Thanks for your message which is a nice food for though Smile | :)
 
I found out that one would surely get the problem if one wants to enter a line-break while working with XML editor of VS.NET IDE. So I must switch to source code view and


replace \n with an {ENTER} hit


WoW Thought I was lazy when thinking about working with txt file.Sleepy | :zzz:

 
Nguyen Anh Tuan
GeneralRe: What about @?sussAnonymous16-Nov-04 2:02 
You can use str.Replace(@"\n","\n");

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130619.1 | Last Updated 29 Apr 2004
Article Copyright 2004 by Heath Stewart
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid