String formatting in C#;
Can I use it? Yes.
Can I implement custom formatting? No.
I need to write something where I can pass a set of custom formatting options to string.Format, which will have some effect on the particular item.
at the moment I have something like this:
string.Format("{0}", item);
but I want to be able to do things with that item:
string.Format("{0:lcase}", item); // lowercases the item
string.Format("{0:ucase}", item); // uppercases the item
string.Format("{0:nospace}", item); // removes spaces
I know I can do things like .ToUpper(), .ToLower() etc. but I need to do it with string formatting.
I've been looking into things like IFormatProvider and IFormattable but I don't really know if they are the things I should be using, or, how to implement them.
Can anyone explain how I can solve this problem?
Rationale (just in case you want to know...)
I have a small program, where I can enter a comma delimited set of values, and a template. The items are passed into string.Format, along with the template which creates an output. I want to provide template formatting options, so that the user can control how they want items to be output.
You can make a custom formatter, something like:
public class MyFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string fmt, object arg, IFormatProvider formatProvider)
{
if(arg == null) return string.Empty;
if(fmt == "lcase")
return arg.ToString().ToLower();
else if(fmt == "ucase")
return arg.ToString().ToUpper();
else if(fmt == "nospace")
return arg.ToString().Replace(" ", "");
// Use this if you want to handle default formatting also
else if (arg is IFormattable)
return ((IFormattable)arg).ToString(fmt, CultureInfo.CurrentCulture);
return arg.ToString();
}
}
Then use it like:
string.Format(new MyFormatter(),
"{0:lcase}, {0:ucase}, {0:nospace}",
"My Test String")
This should return:
my test string, MY TEST STRING, MyTestString
Related
I have to manage a particular string formatting/padding condition. To be short I need to pad a string argument only if the argument length is 0. If I use the typical aligment parameter the padding is made if the length of the argument is smaller then the provided alignment value. For example:
string format = "#{0,10}#";
string argument;
string output;
argument = Console.ReadLine();
output = String.Format(format, argument);
String.WriteLine(output);
If I enter "try" as a value I got this result:
#try #
If I enter "trytrytrytry" I got:
#trytrytrytry#
What I would like to happen is depicted below:
Entering "" I would like to have:
# #
but entering "try" I would like to have:
#try#
The code I'm going to write should be as much generic as possibile since the format parameter is not static and is defined at runtime.
The best practice to do this is to define a custom string formatter. Unluckly it seems that the customization code can only act on the 'format' portion of the format parameter of the String.Format() method.
Indeed If I define a custom formatter:
public class EmptyFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (!this.Equals(formatProvider))
return null;
if (string.IsNullOrEmpty(format))
throw new ArgumentNullException();
return numericString;
}
}
The format and arg parameters of the Format method didn't contain the alignment parameter then I cannot actually check what lenght of the padding value should be applied and therefore I cannot act properly. Moreover this part of the 'formatting' of the string seems to be applied somewhere else but I have not idea where.
Is there a way to 'alter' this behaviour ?
A format item has the following syntax:
index[,alignment][ :formatString] }
The reason the format parameter is null is because there is no formatString component, there is only alignment. I couldn't find a way to access the alignment component from the Format method. However, one (ugly) solution is to set the formatString to be the same as the alignment, so that you can access it using the format parameter:
#{0,10:10}#
A less ugly solution would be to create your own method extension that first extracts the alignment from the given format and then calls the traditional String.Format method.
For example:
public static string StringFormat(this string Arg, string Format) {
//extract alignment from string
Regex reg = new Regex(#"{[-+]?\d+\,[-+]?(\d+)(?::[-+]?\d+)?}");
Match m = reg.Match(Format);
if (m != null) { //check if alignment exists
int allignment = int.Parse(m.Groups[1].Value); //get alignment
Arg = Arg.PadLeft(allignment); //pad left, you can make the length check here
Format = Format.Remove(m.Groups[1].Index - 1, m.Groups[1].Length + 1); //remove alignment from format
}
return (string.Format(Format, Arg));
}
To use the code:
string format = "#{0,10}#";
string argument = "try";
string output = argument.StringFormat(format); //"# try#"
I need to make a possibility to open a webpage either with an additional parameter or without one. If it has a parameter, I'll add it with the string.format function. Sometimes I need to format this parameter before opening the url, however, I don't want to make the code too specific.
The Parameter is always a string and I would need to cut the end of this string. Is it possible to do something in the string to be formatted to cut the unneeded text? If so how would the {0} have to look that it works?
string url = "http://foo.bar/xt:{0}";
string parameter = "abcdefghi";
if (Regex.Matches(Regex.Replace(url,
#"(\{{2}|\}{2})", ""), // removes escaped curly brackets
#"\{(\d+)(?:\:?[^}]*)\}")
.OfType<Match>().Any())
{
Process.Start(string.Format(url, parameter));
}
else
{
Process.Start(url);
}
Instead of abcdefghi I would like to have as parameter just abcdefg for instance. But this should be configurable via url. Something like {0:7) or so...
You have to write this kind of logic yourself. What you can do, however, is wrap this in an extension method so you don't have to duplicate it everywhere:
public static class StringExt
{
public static string Truncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}
}
Now we can write:
var someString = "...";
someString = someString.Truncate(2);
I have a custom formatter specified, basically like this:
public class NotationNumericFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType_)
{
return (formatType_ == typeof(ICustomFormatter) ? this : null;
}
public string Format(string format_, object arg_, IFormatProvider formatProvider_)
{
if (!Equals(formatProvider_) || arg_ == null) // <-- I put a breakpoint here...
{
return;
}
// then a bunch of stuff happens here.
}
}
What's stumping me at the moment is, the following code:
// _myFormatter is a NotationNumericFormatter which gets instanced
// in the ctor of the class in question.
var result = string.Format(_myFormatter, (parameter_ ?? "").ToString(), value_);
Which is never, ever, hitting the first line in my formatter's Format() method. What am I missing here? Is there some subtlety to string.Format that I'm missing?
If parameter does not have {0} then the formatter won't break point
this will breakpoint
var result = string.Format(_myFormatter, "{0}", value_);
this won't
var result = string.Format(_myFormatter, "", value_);
When you call String.Format(IFormatProvider provider , String format , params Object[] args ) the second parameter format hast to specified as a valid format string i.e. it cannot be empty.
Try to set parameter_ to something like "{0}" then it should work.
I was wondering if there's a syntax for formatting NULL values in string.Format, such as what Excel uses
For example, using Excel I could specify a format value of {0:#,000.00;-#,000.00,NULL}, which means display the numeric value as number format if positive, number format in parenthesis if negative, or NULL if the value is null
string.Format("${0:#,000.00;(#,000.00);NULL}", someNumericValue);
Edit
I'm looking for formatting NULL/Nothing values for all data types, not just numeric ones.
My example is actually incorrect because I mistakenly thought Excel used the 3rd parameter if the value was NULL, but it's actually used when the value is 0. I'm leaving it in there because it's the closest thing I can think of to what I was hoping to do.
I am hoping to avoid the null coalescing operator because I am writing log records, and the data is not usually a string
It would be much easier to write something like
Log(string.Format("Value1 changes from {0:NULL} to {1:NULL}",
new object[] { oldObject.SomeValue, newObject.SomeValue }));
than to write
var old = (oldObject.SomeValue == null ? "null" : oldObject.SomeValue.ToString());
var new = (newObject.SomeValue == null ? "null" : newObject.SomeValue.ToString());
Log(string.Format("Value1 changes from {0} to {1}",
new object[] { old, new }));
You can define a custom formatter that returns "NULL" if the value is null and otherwise the default formatted string, e.g.:
foreach (var value in new[] { 123456.78m, -123456.78m, 0m, (decimal?)null })
{
string result = string.Format(
new NullFormat(), "${0:#,000.00;(#,000.00);ZERO}", value);
Console.WriteLine(result);
}
Output:
$123.456,78
$(123.456,78)
$ZERO
$NULL
Custom Formatter:
public class NullFormat : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type service)
{
if (service == typeof(ICustomFormatter))
{
return this;
}
else
{
return null;
}
}
public string Format(string format, object arg, IFormatProvider provider)
{
if (arg == null)
{
return "NULL";
}
IFormattable formattable = arg as IFormattable;
if (formattable != null)
{
return formattable.ToString(format, provider);
}
return arg.ToString();
}
}
I don't think there's anything in String.Format that will let you specify a particular format for null strings. A workaround is to use the null-coalescing operator, like this:
const string DefaultValue = "(null)";
string s = null;
string formatted = String.Format("{0}", s ?? DefaultValue);
Is this what you want?
string test;
test ?? "NULL"
It looks like String.Format for .NET acts the same way as Excel, i.e., you can use ; separator for positive, negative, and 0 values, but not NULL: http://msdn.microsoft.com/en-us/library/0c899ak8.aspx#SectionSeparator.
You will probably just have to handle the null value manually:
if (myval == null)
// handle
else
return String.Format(...);
You could use an extension method:
public static string ToDataString(this string prm)
{
if (prm == null)
{
return "NULL";
}
else
{
return "'" + prm.Replace("'", "''") + "'";
}
}
Then in your code you can do:
string Field1="Val";
string Field2=null;
string s = string.Format("Set Value:{0}, NullValue={1}",Field1.ToDataString(), Field2.ToDataString());
I am writing a app in .NET which will generate random text based on some input. So if I have text like "I love your {lovely|nice|great} dress" I want to choose randomly from lovely/nice/great and use that in text. Any suggestions in C# or VB.NET are welcome.
You could do it using a regex to make a replacement for each {...}. The Regex.Replace function can take a MatchEvaluator which can do the logic for selecting a random value from the choices:
Random random = new Random();
string s = "I love your {lovely|nice|great} dress";
s = Regex.Replace(s, #"\{(.*?)\}", match => {
string[] options = match.Groups[1].Value.Split('|');
int index = random.Next(options.Length);
return options[index];
});
Console.WriteLine(s);
Example output:
I love your lovely dress
Update: Translated to VB.NET automatically using .NET Reflector:
Dim random As New Random
Dim s As String = "I love your {lovely|nice|great} dress"
s = Regex.Replace(s, "\{(.*?)\}", Function (ByVal match As Match)
Dim options As String() = match.Groups.Item(1).Value.Split(New Char() { "|"c })
Dim index As Integer = random.Next(options.Length)
Return options(index)
End Function)
This may be a bit of an abuse of the custom formatting functionality available through the ICustomFormatter and IFormatProvider interfaces, but you could do something like this:
public class ListSelectionFormatter : IFormatProvider, ICustomFormatter
{
#region IFormatProvider Members
public object GetFormat(Type formatType)
{
if (typeof(ICustomFormatter).IsAssignableFrom(formatType))
return this;
else
return null;
}
#endregion
#region ICustomFormatter Members
public string Format(string format, object arg, IFormatProvider formatProvider)
{
string[] values = format.Split('|');
if (values == null || values.Length == 0)
throw new FormatException("The format is invalid. At least one value must be specified.");
if (arg is int)
return values[(int)arg];
else if (arg is Random)
return values[(arg as Random).Next(values.Length)];
else if (arg is ISelectionPicker)
return (arg as ISelectionPicker).Pick(values);
else
throw new FormatException("The argument is invalid.");
}
#endregion
}
public interface ISelectionPicker
{
string Pick(string[] values);
}
public class RandomSelectionPicker : ISelectionPicker
{
Random rng = new Random();
public string Pick(string[] values)
{
// use whatever logic is desired here to choose the correct value
return values[rng.Next(values.Length)];
}
}
class Stuff
{
public static void DoStuff()
{
RandomSelectionPicker picker = new RandomSelectionPicker();
string result = string.Format(new ListSelectionFormatter(), "I am feeling {0:funky|great|lousy}. I should eat {1:a banana|cereal|cardboard}.", picker, picker);
}
}
String.Format("static text {0} more text {1}", randomChoice0, randomChoice1);
write a simple parser that will get the information in the braces, split it with string.Split , get a random index for that array and build up the string again.
use the StringBuilder for building the result due to performance issues with other stringoperations.