5

Is there a way to String.Format a message without having to specify {1}, {2}, etc? Is it possible to have some form of auto-increment? (Similar to plain old printf)

Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
  • What's the purpose of not using numbers? Maybe if we can follow the reason why you want to avoid using them, we assist with a more useful solution... – BenAlabaster Feb 18 '09 at 14:01
  • 4
    I have about 20 parameters, and writing a number for each one is cumbersome. I'm also afraid I may miss a number, or use a number twice. And I feel that maintainability without numbers may be easier. – Hosam Aly Feb 18 '09 at 14:15
  • Understandable I suppose... I always find it laborious if I want to embed yet to be formatted strings into other yet to be formatted strings...and then append the params at the end of the process which also has the same drawbacks. – BenAlabaster Feb 18 '09 at 15:41
  • Using indices, you are not bound to the order of the supplied parameters. This is good when you want to translated that format string. – Hans Kesting Jul 05 '14 at 14:09
  • With C#7, answers are obsolete, as it includes ["string interpolation"](https://learn.microsoft.com/fr-fr/dotnet/csharp/language-reference/tokens/interpolated) as in Perl: `$"{name} is {age} year{(age == 1 ? "" : "s")} old."` --> *Horace is 34 years old*. – mins Mar 24 '19 at 10:55

7 Answers7

5

You can use a named string formatting solution, which may solve your problems.

Community
  • 1
  • 1
cbp
  • 25,252
  • 29
  • 125
  • 205
2

Afraid not -- where would it put the objects into the string? Using printf, you still need to put specifiers in somewhere.

mqp
  • 70,359
  • 14
  • 95
  • 123
2

I think the best way would be passing the property names instead of Numbers. use this Method:

using System.Text.RegularExpressions;
using System.ComponentModel;

public static string StringWithParameter(string format, object args)
    {
        Regex r = new Regex(@"\{([A-Za-z0-9_]+)\}");

        MatchCollection m = r.Matches(format);

        var properties = TypeDescriptor.GetProperties(args);

        foreach (Match item in m)
        {
            try
            {
                string propertyName = item.Groups[1].Value;
                format = format.Replace(item.Value, properties[propertyName].GetValue(args).ToString());
            }
            catch
            {
                throw new FormatException("The string format is not valid");
            }
        }

        return format;
    }

Imagine you have a Student Class with properties like: Name, LastName, BirthDateYear and use it like:

 Student S = new Student("Peter", "Griffin", 1960);
 string str =  StringWithParameter("{Name} {LastName} Born in {BithDate} Passed 4th grade", S);

and you'll get: Peter Griffin born in 1960 passed 4th grade.

Ashkan Ghodrat
  • 3,162
  • 2
  • 32
  • 36
1

There is a C# implementation of printf available here

Rune Grimstad
  • 35,612
  • 10
  • 61
  • 76
0

If someone is interested, I have modified Ashkan's solution to be able to run it under WinRT:

/// <summary>
/// Formats the log entry.
/// /// Taken from:
/// http://stackoverflow.com/questions/561125/can-i-pass-parameters-to-string-format-without-specifying-numbers
/// and adapted to WINRT
/// </summary>
/// <param name="format">The format.</param>
/// <param name="args">The arguments.</param>
/// <returns></returns>
/// <exception cref="System.FormatException">The string format is not valid</exception>
public static string FormatLogEntry(string format, object args)
{
    Regex r = new Regex(@"\{([A-Za-z0-9_]+)\}");

    MatchCollection m = r.Matches(format);

    var properties = args.GetType().GetTypeInfo().DeclaredProperties;

    foreach (Match item in m)
    {
        try
        {
            string propertyName = item.Groups[1].Value;
            format = format.Replace(item.Value, properties.Where(p=>p.Name.Equals(propertyName))
                .FirstOrDefault().GetValue(args).ToString());
        }
        catch
        {
            throw new FormatException("The string format is not valid");
        }
    }

    return format;
}
Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
awsomedevsigner
  • 150
  • 1
  • 7
0

One could always use this (untested) method, but I feel it's over complex:

public static string Format(char splitChar, string format,
                            params object[] args)
{
    string splitStr = splitChar.ToString();
    StringBuilder str = new StringBuilder(format + args.Length * 2);
    for (int i = 0; i < str.Length; ++i)
    {
        if (str[i] == splitChar)
        {
            string index = "{" + i + "}";
            str.Replace(splitStr, index, i, 1);
            i += index.Length - 1;
        }
    }

    return String.Format(str.ToString(), args);
}
Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
0

I came up with this, again it's a bit cumbersome but it works fine for what I needed to do which was to pass a variable number or arguments to my own function in the same way as I'd use WriteLine. I hope it helps somebody

protected void execute(String sql, params object[] args)
{
    for (int i = 0; i < args.Count(); i++ )
    {
        sql = sql.Replace(String.Format("{{{0}}}", i), args[i].ToString());
    }
    //...
}
  • could you please tell me in which program you have implemented that? I'd like to do some SQL injection. So to be honest you should really think about replacing this by using the [`SqlParameter`](http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparameter.aspx) within the [`SqlCommand`](http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx) – Oliver Jan 24 '12 at 09:51