Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

code better – use string.format

Poorly handled exceptions might speak volumes about someone’s coding abilities, but it’s string concatenation that’s a sure bet to kill a programs readability (thus maintainability). Everyone knows that they should use StringBuilder’s for better performance when concatenating a lot, but to improve maintainability, string.format is king!

On the topic of performance though, you’ll be glad to know that there’s also an AppendFormat method to the stringbuilder class – so readability and performance aren’t exclusive concepts!

Surely, I can’t be the only one that has a hard time writing and maintaining code like:
document.SelectSingleNode(“/graph/data[name=’” + name + “‘]“);

When I do write code like the above, I almost always forget my closing quote or square bracket! And as things get more complicated, it becomes a flat out nightmare.

The solution is to make heavy use of string.Format. You’ll never EVER see me use + (or & in VB.NET) to concatenate something to a string, and there’s no reason you should either. To write the above code better, try:
document.SelectSingleNode(string.Format(“/graph/data[name='{0}’]“, name));

Personally, even though it’s a simple example, I find it a lot better – and as things get more complicated you’ll be laughing as you code. But wait, there’s more!  string.Format uses string formatting – so you have a lot of control over how objects are turned into strings. So you coul do
string.Format(“Your score is {0:p}“, student.FinalExam.Score); 
which would format the float score into a percentage.

Understanding .NET String formatting is important because (a) it’ll make it so you never ask a question about how to get a date in a certain format, (b) how to get a number in a certain format and (c) it’s used all over in .NET.  For example, that DataBinder.Eval() method actually accepts a 3rd parameter which is the string formatter to use.

Now, like I said earlier, you can also use AppendFormat of the StringBuilder class

StringBuilder sb = new StringBuilder();
sb.Append(“<books>”);
foreach(Book book in author.Books)
{
   sb.AppendFormat(“<book isbn=\”{0}\”>{1}</book>”, book.Isbn, book.Name);
}
sb.Append(“</books>”);

A word of caution. String.format IS NOT a substitute for using command parameters. But dang, it is sweet for almost everything else!

This entry was posted in Short Outbursts. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

40 Responses to code better – use string.format

  1. Matthew says:

    By the way, String.Format does not have a monopoly on string formatting. You can also specify a format for a single object to its ToString() method. So, for instance, this:

    string s = “0x” + 16.ToString(“x”);

    accomplishes the same thing as:

    string s = String.Format(“0x{0:x}”, 16);

  2. Matthew says:

    Really? You love String.Format that much? Put performance aside for a moment. I contest that readability is not improved by String.Format, even though I am a die-hard C programmer who used to love printf().

    The problem with these substitution routines is that they separate the symbol from the variable. You’re stuck seeing cryptic {0}, {1}, {2}, {3}, {4}, … and then you only find out at the end of the statement which variables mean what. Order is critical. If you mess up that, then you can introduce some subtle bugs.

    Really, I do think this:

    Console.WriteLine(
    “number: ” + i.ToString() + “.” +
    “number + 1: ” + (i + 1).ToString() + “.” +
    “number + 2: ” + (i + 2).ToString() + “.”
    );

    is more clear than this mess:

    Console.WriteLine(
    String.Format(
    “number: {0}.number + 1: {1}.number + 2: {2}.”,
    i, i + 1, i + 2
    )
    );

  3. Chris says:

    OMG – if you’re really writing XML like that you need help!

  4. AnswerCage says:

    Thank you Brian, i’ve setup a test for the three Methods +,stringbuiler and string.format and you’re right.

  5. parke says:

    Thank you…

  6. Brian Hill says:

    I know this blog is a little old, but I found it while researching some string formatting questions. I’d hate to think that some junior developer would find this and think that String.Format had performance problems. That’s just not true. The truth is that String.Format performs better.

    I’m posting here regarding Betta’s comment. The truth to this is that Betta’s example doesn’t actually test String.Format against basic string concatenation. Here is a quote from MSDN on System.String:

    “A String is called immutable because its value cannot be modified once it has been created. Methods that appear to modify a String actually return a new String containing the modification. If it is necessary to modify the actual contents of a string-like object, use the System.Text.StringBuilder class.”

    This means the even doing a “some string” + someVariable has to create a new instance of the final output. Because the example Betta used didn’t actually change the string for each iteration, the complier is smart enough to know this and only has to execute the line (string a = “SELECT * FROM TTT WHERE UserID = ‘” + userID + “‘ ORDER BY BBB DESC LIMIT 1″;) once, instead of 5000000 times. To really test the performance of String.Format you have to change the example to use “WHERE UserID=” + i. This changes the string during each actual iteration. Running that test, String.Format actually comes out quite a bit faster (on my box over ½ a second faster). This is because String.Format is doing the same thing under the covers that StringBuilder does.

  7. karl says:

    Paul:
    string.Concat is useful because of it’s speed, but it isn’t much more readable than using +.

    I use string.Concat a lot when dealing with 1 (maybe 2) variables:

    string cacheKey = string.Concat(“userId:”, userId);

  8. paul says:

    hi kerl,

    what is the use of string concat

  9. Jonas says:

    I agree on all you are saying about using
    string.Format.

    But I find it “ironic” that you end the post with string building XML.

    What will happen when someone retreives the book; book.Name == “101 ? & Answers”

    Thanks for a good article
    /Jonas

  10. I totally agree with Karl, that String.Format is much more readable. Here is my post on why: http://www.sharpdeveloper.net/content/archive/2007/05/25/use-string.format-instead-of-chopping-strings.aspx

  11. psmithphil says:

    I just want to thank the author for an excellent article. This has opened up a new world for strings for me.

  12. Betta Glurpse says:

    Great to hear. There is nothing worse than people who categorically dismiss arguments that don’t fit with their own preference – thankfully, you don’t seem to be one of them. And yes, more readable quoting is one of the pros of using String.Format.

    On another note, I absolutely agree my example isn’t good database programming (with vulnerability to SQL injection being just one reason), but I think it is a decent representative “format” string. Many error messages, which probably accounts for a large portion of formatted strings, are “parameterized” in a similar fashion.

  13. karl says:

    Betta:
    I agree that in some cases it might be worse off. But I do think that when you get into a lot of escaping, using + is a nightmare. Sticking with your not-so-good SQL example (since parameterized queries are better than both approaches)…

    “insert into users (username, password, type) values (‘” + name + “‘, ‘” + password + “‘, ” + type +”)”

    isn’t fun to write. I forgot a couple +’s writing this out the first time and have always found it hard to read “‘

    again, the SQL query example isn’t the best though…

  14. Betta Glurpse says:

    I disagree. I admit that in your short one-argument example using quotes, the String.Format version is slightly more readable. But most often, I find that String.Format is less maintainable compared to concatenation with +. The main reason is that with ordinary concatenation, the arguments are kept where they are used, namely inside the string. This is good.

    Consider this:

    db.executeStatement(String.Format(“UPDATE {0} SET owner_id={1} where owner_id is null and {2}”, name, id, whereClause));

    This is bad for two reasons:

    1) MENTAL OVERHEAD CORRELATING PARAMETERS TO ARGUMENTS: When checking this statement, I have to remember that argument 0 is the table name, 1 is the owner id and so on until I reach the argument list and can see that there is a match. In particular, this makes it harder to decipher what poorly named variables such as “name” and “id” in the example above are being used for.

    2) RENUMBERING OR DISORDER: If for some reason I need to set an additional column value in the example above, I have to either renumber the folowing arguments, or accept further mental overhead because the arguments are no longer used in the order they appear:

    db.executeStatement(String.Format(“UPDATE {0} SET owner_id={1}, ttime={3} where owner_id is null and {2}”, name, id, whereClause, transactiontime));

  15. karl says:

    Would you be able to post a sample project somewhere in a zip?

    Have you tried a profiler,such as RedGate’s Ants or JetBrain’s dotTrace? I think they both have fully functional trials…

  16. Neil says:

    I’m having trouble converting a long (125kb…not really that long is it?) byte array (that I read from a binary file using ReadAllBytes) to a string in under 10 minutes… I’ve tried several methods (including BitConverter), but however I do it there’s a bottleneck when building the string:/

    As you say, StringBuilder.Append works much faster than concantenating, but I still need to convert the stringbuilder back to a string at some point in order to use Contains and SubString later on to process the file – at which point the StringBuilder.ToString takes ages!

    Does anyone have any ideas?
    Cheers

  17. lb says:

    top article Karl!

    i’m a fan of string.Format but when simply concatenating a small number of strings together, i prefer string.Concat

    it’s faster under the hood than string.format because it doesn’t need to parse the string for arguments.

    so there’s four important tools here, each with their own niche:

    string.format
    string.concat
    stringbuilder.append
    stringbuilder.appendformat

    that way you get performance/flexibility in just the right amount.

    if you use string.format where string.Concat is more apt you do take an unwanted performance hit.

    Always enjoy your stuff, Karl.

    lb

  18. Imran Aziz says:

    nice one, I get into formating issues so many times specially while creating html in code, its nice to be reminded about using something that is already supported well by the language.

  19. Tony B says:

    A quick Google (trying to see what the real impact on perf is)

    My favorite perf guru: http://blogs.msdn.com/ricom/archive/2004/03/12/88715.aspx

  20. DaRage says:

    Travis, milliseconds don’t add up quickly and to believe so is mere pathetic paranoia. let the cpu do its work and let the developers to do theirs.

  21. Haacked says:

    Right.. because I heard that string.format takes around two hours to properly format a string.

  22. Travis001 says:

    I find it somewhat funny that given the extremely slow performance of the String.Format function anyone would want to use it.

    To say that as developer it is more important that we have pretty code then to have a function that takes between 4-6 times longer is pathetic. I believe that you can have nice looking code that is also concerned about performance.

    Just remember that if you are writing good software you actually have customers using it and those milliseconds add up quickly.

  23. Paul Chu says:

    I was getting an error too with the embedded braces.

    I added additional parameters to insert the braces with String.Format
    {0} –> “{”
    {1} –> “}”

    function x()
    {0}
    alert(‘test’);
    {1}

    Regards, Paul
    paulchu8@gmail.com

  24. karl says:

    Brian:
    The problem is that you have a { at the end of your 3rd example. { is a special character in string.Format, so it’s causing it to have a hicup.

    You should be able to escape the { with a double {{, ala:
    string input = “\tprivate void Q{0}_SelectedIndexChanged(object sender, System.EventArgs e) {{“;

    but that doesn’t seem to work, and I honestly can’t put my finger on why this morning…

    This works:
    string input = “\tprivate void Q{0}_SelectedIndexChanged(object sender, System.EventArgs e) {1}”;
    string.Format(input, 123, “{{“);

    which is a little weird. I’ll try to look more into it.

  25. Brian says:

    I originally thought there was a character limit issue that I was encountering (because my 3rd string was actually a big block of .NET code that I was trying to format.

    But even the shortened format from my above example will fail. So I don’t know what I’m doing wrong.

    Thanks for any and all of your help!

    Brian

  26. Brian says:

    Here’s my trouble:

    I have a little .NET Windows Forms utility that I’m working on that helps generate my C# code-behind files for an ASP.NET application.

    I have three different strings I’m trying to format. The first two strings work just fine, then the 3rd string gives me this System.FormatException error:
    “Input string was not in a correct format.”

    //String #1: (works ok)
    string input = “\t\tprotected System.Web.UI.WebControls.CheckBoxList Q{0};”;

    //String #2: (works ok)
    string input = “this.Q{0}.SelectedIndexChanged += new System.EventHandler(this.Q{0}_SelectedIndexChanged);”;

    //String #3: (error raised)
    string input = “\tprivate void Q{0}_SelectedIndexChanged(object sender, System.EventArgs e) {“;

    Here’s my syntax for formatting the above INPUT strings (which in my actual code are pulled out of a database into objects):

    String.Format(input,param);

    //the param in these cases are an Int32.ToString() which I am wanting to insert.

  27. karl says:

    Brian:
    I’m under the strong impressions that string’s in .NET have no real upper limit, other than what the OS (32 bits) imposes and whatever overhead .NET has (like the fact that all chars are unicode, so cut that in 1/2).

  28. Brian says:

    Is there a character limit on the target string which is being formatted?

  29. I use String.Format everywhere! And infact, it uses StringBuilder under-the-hood:

    public static string Format(IFormatProvider provider, string format, params object[] args)
    {
    if ((format == null) || (args == null))
    {
    throw new ArgumentNullException((format == null) ? “format” : “args”);
    }
    StringBuilder builder1 = new StringBuilder(format.Length + (args.Length * 8));
    builder1.AppendFormat(provider, format, args);
    return builder1.ToString();
    }

    Cheers.

  30. Pecha,
    Just to note, the first version doesn’t do anything, the compiler concantated in in compile time.

  31. pecha says:

    I absolutely agree with you, string.Format this is a very nice feature. I just showed small example, maybe for someone it will be interesting.

  32. karl says:

    I did say you should never use string.Format in lieu of command parameters. Anyways, that point aside, I guess you should always take into account performance, but almost anyone should be willing to pay the price for a 6 second delay over 5 million iteration for varstly improved readability.

    Remember, you can always use the AppendFormat method of the StringBuilder for a lot of concatenation..works just like string.Format..

  33. pecha says:

    Nice article! But few days ago I found that string.Format works very slow. Example with + .. + (see below) executes in 1 sec, with string.Format this is about 6 sec!! in VS 2005.

    string userID = "ccf5acf6-2d28-4077-83d4-9fa6685057d4";

    Console.WriteLine(DateTime.Now.ToLongTimeString());

    for (int i = 0; i < 5000000; i++)

    {

       string a = "SELECT * FROM TTT WHERE UserID = ‘" + userID + "’ ORDER BY BBB DESC LIMIT 1";

    }

    Console.WriteLine(DateTime.Now.ToLongTimeString());

    Console.WriteLine("———————————");

    Console.WriteLine(DateTime.Now.ToLongTimeString());

    for (int i = 0; i < 5000000; i++)

    {

       string a = String.Format("SELECT * FROM TTT WHERE UserID = ‘{0}’ ORDER BY BBB DESC LIMIT 1", userID);

    }

    Console.WriteLine(DateTime.Now.ToLongTimeString());

    Console.Read();

  34. karl says:

    GR KHAN:
    Using the out-of-the-box Globalization features of .NET,it’s only possible to localize currency symbol, decimal separators, and other numeric symbols. I don’t believe there’s a straightfoward way to convert a Int32 into a localized version of the # unfortunetly (though I could be wrong).

    This is the best I could find which might help you:
    http://www.codeproject.com/csharp/num_to_arabic.asp

  35. johnpapa says:

    Good post, Karl. I generally use string.Format(…) for 3 (+/-) or less concats. Otherwise I use Stringbuilder. The plus or minus is there because I use the good ol’ eyeball technique to make sure it passes the readability test :)

  36. GR KHAN says:

    Please give code that how we convert numbers into arabic language.
    Thanks,

  37. programer says:

    good article,study

  38. Sharbel says:

    Good article

    String.Format() is fantastic for 2-4 concats. Also, i recall performace tests showing StringBuilders were a waste for under 5 concats, and using String.Format() is perfectly acceptable.

  39. Rajiv Menon says:

    Hey Karl,
    Here’s a cool article on string formatting from Kathy.
    link: http://blogs.msdn.com/kathykam/archive/2006/03/29/564426.aspx