(How)
C^# You Are - 6 January 2008
This week's questions revolve around formatting values into strings. Irrelevan of the usage
of a program it is inevitable that values need to be formatted. Some formatting is simple but
most useful ones require a little more thought.
- How do you format a number as a percentage with two decimals of precision? Answer
The format specifier for a percentage is simply p or P. To add precision to most numeric specifiers append a number to the end.
The specifier automatically adds a percentage sign (technically the locale specific percentage symbol). Additionally the number
is multiplied by 100 so 0.45 is translated to 45%.
float var = 0.34567F;
string str = var.ToString("P2"); //34.57 %
- How do you format a number as a monetary value? Answer
The specifier for monetary values is C or c. It automatically includes the appropriate currency sign and two decimals of precision. To change
the precision include a number after the C, such as C2.
float var = 45.678F;
string str = var.ToString("C"); //$45.68
- How do you format a time using the 24-hour format? Answer
Time is formatted, by default, using the approriate locale time. To generate a time using 24 hour format you have to use custom formatting and
build the string up manually. h is used for the 12-hour times while H is used for 24 hour times. As you would expect m is
used for minutes while s is used for seconds. If you want to display AM/PM then use t
string str24 = DateTime.Now.ToString("HH:mm:ss"); //18:55:05
string str12 = DateTime.Now.ToString("h:mm:ss tt"); //6:55:05 PM
- How do you format a number in a readable format including a negative sign (if needed), commas and 3 decimals of precision? Answer
The specifier F will give you the negative sign and decimal but not the commas (or the appropriate locale symbol). Using N will format
the number with commas.
float var = -123456.789F;
string str = var.ToString("f2"); // 123456.80
string str2 = var.ToString("n2"); // 123,456.80
- You read a (valid) numeric value from the user and store it as a string internally. Later you need to get the numeric value
back but you find the conversion back is returning a different value. How can you ensure that the numeric value you convert to a string
can be retrieved back properly? Answer
Contrary to popular belief, converting a value to a string does not guarantee that it can be converted back. This is a common mistake many people make. There are
many people in the forums who believe that if it works for some types, or even some of the time, then it should always work but that simply isn't true. To round-trip
a number to a string and back use the r specifiers. The specifier does have higher overhead than standard numeric specifiers and it will ignore precision requests. In
general you will not use the specifier for user-visible values. Instead use it when you need to convert to a string for storage.
double var = 73556.688051054574;>
string strNoGuarantee = var.ToString(""); //73556.6880510546
string strGuaranteed = var.ToString("r2"); //73556.688051054574
- How do you display a 32-bit hexadecimal value? Answer
To display a number in hex use the x or X specifier. If you use a precision specifier (a number after the specifier) then the value is prepended with
zeroes until it is the appropriate length. The following example formats a number as a 32-bit hex value.
int var = 12345;
string str = var.ToString("X8"); //00003039
int var2 = 0x12345;
string str2 = var2.Tostring("X8"); //00012345
- You need to format numbers in a table to display on the screen. You want each number to take up 3 characters even if the number is not that long. How do you do that? Answer
The actual format of a formatting specifier is {index[,alignment][:specifier]}. The alignment is a number that specifies the total size, in characters, of the
final value. The value can be longer but it will never be shorter. If the value isn't large enough then it is right justified with spaces. If the alignment is negative then
the value is left justified.
This is a rare situation where you can't use ToString for this. ToString does not allow the specification of the index nor the alignment. You have to
use String.Format instead.
int[] numbers = new int[100];
for (int nIdx = 0; nIdx < 100; ++nIdx)
numbers[nIdx] = nIdx;
// 0
// 1
//...
// 10
//...
// 99
string str;
foreach (int number in numbers)
str = string.Format("{0,3}", number);
- You want to format positive numbers normally but negative numbers you want to wrap in parenthesis. How can you do that? Answer
When dealing with numeric values the format specifiers allows up to three specifiers (separated by a semicolon). The first specifiers applies to positive values. If you specify two specifiers then the first
specifier is used for positives and the second for negatives. If you specify three then they apply to positives, zero and negatives, respectively.
float positive = 123F;
float negative = -123F;
string str = positive.ToString("###;(###)"); //123
string str2 = negative.ToString("###;(###)"); //(123)
Notice that we did not use the standard formatters in the example. The standard formatters do not work in this case. Rather you have to use the custom formatters where you
specify each digit and symbol to display. Inconvenient, but it works.
- You need to store a date and time value such that you can restore it later (including timezone information). How do you do it? Answer
The o specifier stores the date and time in a standard format with GMT offset information so it can be restored later.
string strFormal = DateTime.Now.ToString("o"); //2008-01-06T18:58:16.5673145-05:00
The above example says that the time is January 6th, 2008 at approximately 6:58 PM, EST.
- You want to display the textual version of an enumerated value. How can you do it? Answer
The default behavior when converting an enumeration to a string is to generate the textual version. However some things can mess it up. If the enumerated value
is a mix of values then the numeric value is displayed instead unless you use the FlagsAttribute and the bitmask is valid. However if you use
the f specifier then the formatter will always try to convert the numeric value to a string irrelevant of the attribute.
enum SomeOptions
{
Option1 = 1,
Option2 = 2,
Option4 = 4,
}
...
SomeOptions opts = Option1 | Option2;
string strNoFlags = opts.ToString(); //3
string strWithGSpecifier = opts.ToString("g"); //Option1, Option2