How to Create a More User-Friendly String.Format Syntax

How can I create a more user-friendly string.format syntax?

How about the following, which works both for anonymous types (the example below), or regular types (domain entities, etc):

static void Main()
{
string s = Format("You are {age} years old and your last name is {name} ",
new {age = 18, name = "Foo"});
}

using:

static readonly Regex rePattern = new Regex(
@"(\{+)([^\}]+)(\}+)", RegexOptions.Compiled);
static string Format(string pattern, object template)
{
if (template == null) throw new ArgumentNullException();
Type type = template.GetType();
var cache = new Dictionary<string, string>();
return rePattern.Replace(pattern, match =>
{
int lCount = match.Groups[1].Value.Length,
rCount = match.Groups[3].Value.Length;
if ((lCount % 2) != (rCount % 2)) throw new InvalidOperationException("Unbalanced braces");
string lBrace = lCount == 1 ? "" : new string('{', lCount / 2),
rBrace = rCount == 1 ? "" : new string('}', rCount / 2);

string key = match.Groups[2].Value, value;
if(lCount % 2 == 0) {
value = key;
} else {
if (!cache.TryGetValue(key, out value))
{
var prop = type.GetProperty(key);
if (prop == null)
{
throw new ArgumentException("Not found: " + key, "pattern");
}
value = Convert.ToString(prop.GetValue(template, null));
cache.Add(key, value);
}
}
return lBrace + value + rBrace;
});
}

string format custom parameter

In c#-6.0 you can use

string result = $"Time: {DateTime.Now}";

On previous versions see

  • How to replace tokens on a string template?

How to create a String with format?

I think this could help you:

import Foundation

let timeNow = time(nil)
let aStr = String(format: "%@%x", "timeNow in hex: ", timeNow)
print(aStr)

Example result:

timeNow in hex: 5cdc9c8d

What is the most readable use of String.Format for long strings with many parameters?

You could look at the StringBuilder class and split the assembly of the string over several lines.

The AppendFormat method (thanks Joel) is what you want in this case.

Is it better practice to use String.format over string Concatenation in Java?

I'd suggest that it is better practice to use String.format(). The main reason is that String.format() can be more easily localised with text loaded from resource files whereas concatenation can't be localised without producing a new executable with different code for each language.

If you plan on your app being localisable you should also get into the habit of specifying argument positions for your format tokens as well:

"Hello %1$s the time is %2$t"

This can then be localised and have the name and time tokens swapped without requiring a recompile of the executable to account for the different ordering. With argument positions you can also re-use the same argument without passing it into the function twice:

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)

How to format strings in Java

In addition to String.format, also take a look java.text.MessageFormat. The format less terse and a bit closer to the C# example you've provided and you can use it for parsing as well.

For example:

int someNumber = 42;
String someString = "foobar";
Object[] args = {new Long(someNumber), someString};
MessageFormat fmt = new MessageFormat("String is \"{1}\", number is {0}.");
System.out.println(fmt.format(args));

A nicer example takes advantage of the varargs and autoboxing improvements in Java 1.5 and turns the above into a one-liner:

MessageFormat.format("String is \"{1}\", number is {0}.", 42, "foobar");

MessageFormat is a little bit nicer for doing i18nized plurals with the choice modifier. To specify a message that correctly uses the singular form when a variable is 1 and plural otherwise, you can do something like this:

String formatString = "there were {0} {0,choice,0#objects|1#object|1<objects}";
MessageFormat fmt = new MessageFormat(formatString);
fmt.format(new Object[] { new Long(numberOfObjects) });

Regular expression for String.Format-like utility

You can use a regex to match a balanced pair, then figure out what to do with the braces. Remember that .NET regexs aren't "regular".

class Program {
static void Main(string[] args) {
var d = new Dictionary<string, string> { { "Name", "World" } };
var t = new Test();
Console.WriteLine(t.Replace("Hello {Name}", d));
Console.WriteLine(t.Replace("Hello {{Name}}", d));
Console.WriteLine(t.Replace("Hello {{{Name}}}", d));
Console.WriteLine(t.Replace("Hello {{{{Name}}}}", d));
Console.ReadKey();
}
}

class Test {

private Regex MatchNested = new Regex(
@"\{ (?>
([^{}]+)
| \{ (?<D>)
| \} (?<-D>)
)*
(?(D)(?!))
\}",
RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
| RegexOptions.Singleline);

public string Replace(string input, Dictionary<string, string> vars) {
Matcher matcher = new Matcher(vars);
return MatchNested.Replace(input, matcher.Replace);
}

private class Matcher {

private Dictionary<string, string> Vars;

public Matcher(Dictionary<string, string> vars) {
Vars = vars;
}

public string Replace(Match m) {
string name = m.Groups[1].Value;
int length = (m.Groups[0].Length - name.Length) / 2;
string inner = (length % 2) == 0 ? name : Vars[name];
return MakeString(inner, length / 2);
}

private string MakeString(string inner, int braceCount) {
StringBuilder sb = new StringBuilder(inner.Length + (braceCount * 2));
sb.Append('{', braceCount);
sb.Append(inner);
sb.Append('}', braceCount);
return sb.ToString();
}

}

}

Format a Go string without printing?

Sprintf is what you are looking for.

Example

fmt.Sprintf("foo: %s", bar)

You can also see it in use in the Errors example as part of "A Tour of Go."

return fmt.Sprintf("at %v, %s", e.When, e.What)

Which are safe methods and practices for string formatting with user input in Python 3?

It doesn't matter which format you choose, any format and library can have its own downsides and vulnerabilities. The bigger questions you need to ask yourself is what is the risk factor and the scenario you are facing with, and what are you going to do about it.
First ask yourself: will there be a scenario where a user or an external entity of some kind (for example - an external system) sends you a format string? If the answer is no, there is no risk. If the answer is yes, you need to see whether this is needed or not. If not - remove it to eliminate the risk.
If you need it - you can perform whitelist-based input validation and exclude all format-specific special characters from the list of permitted characters, in order to eliminate the risk. For example, no format string can pass the ^[a-zA-Z0-9\s]*$ generic regular expression.

So the bottom line is: it doesn't matter which format string type you use, what's really important is what do you do with it and how can you reduce and eliminate the risk of it being tampered.



Related Topics



Leave a reply



Submit