Is it possible in .NET to have the most compact number formatting like "G"eneral Number formatting, with extra thousands separator.
I can't use the following
String.Format("{0:#,###.###}",32445.324777M)
Because I get "32,445.325", and instead the result I want should be "32.445,325777". Is should also work with an arbitrary number of significant digits in the fractional part.
PS: I only need this for decimals.
That's where formatting culture comes in. You need to get a format specifier that matches your requirements. The default you have is usually the current culture, UI culture or invariant culture. The results you're getting imply you're using the US culture.
If you have a specific culture you want to output the number in, use that. If not, you can create your own:
var nfi =
new NumberFormatInfo
{
NumberDecimalSeparator = ",",
NumberGroupSeparator = "."
};
var ci =
new CultureInfo(CultureInfo.InvariantCulture.LCID) { NumberFormat = nfi };
return string.Format(ci, "{0:#,###.########}", 32445.324777M)
If you want to also get the most compact number, you'll have to use your own code. The easiest way would be to try both, and return the smaller resulting string.
If you want to, you can still use the string.Format syntax too - you can code your own ICustomFormatter to handle that:
void Main()
{
var number = 32445.324777M;
string.Format(new MyNumberFormatter(), "{0:MyG}", number).Dump();
}
class MyNumberFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type type)
{
return this;
}
public string Format(string fmt, object arg, IFormatProvider formatProvider)
{
if (fmt != "MyG" || !(arg is decimal)) return string.Format(CultureInfo.CurrentCulture, "{0:" + fmt + "}", arg);
return "Hi";
}
}
This implementation is somewhat hacky, of course, I'm sure you can find better examples. But it does work. In the Format method, you can choose the format that fits better for the given number, or even just try something like doing the usual ToString("G", CultureInfo.InvariantCulture) and adding the decimal separators to that string. Whatever floats your boat :)
From the .NET documentation
The "#" custom format specifier serves as a digit-placeholder symbol.
If the value that is being formatted has a digit in the position where
the "#" symbol appears in the format string, that digit is copied to
the result string.
Otherwise, nothing is stored in that position in
the result string. Note that this specifier never displays a zero that
is not a significant digit, even if zero is the only digit in the
string. It will display zero only if it is a significant digit in the
number that is being displayed.
The "##" format string causes the
value to be rounded to the nearest digit preceding the decimal, where
rounding away from zero is always used. For example, formatting 34.5
with "##" would result in the value 35.
It's not possible to format an unspecified amount of decimal places with the default formatting possibilities. So you should consider writing your own implementation if needed.
Also about the decimal and thousands separator, it depends upon your system settings, but you can override them by using a different culture as #Luaan described it in his answer.
You should also probably look into this answer.
If you want full control create yoru own formatter lik below. See case "U" for your format.
public class CustomerFormatter : 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;
}
else
{
// generic formatter if no formater specified
if (String.IsNullOrEmpty(format))
format = "G";
// not a decimal type object
if (!(arg is decimal))
return null;
// get value
decimal val = (decimal)arg;
// convert value into generic culture string for control of format
string valueString = val.ToString();
// get string in required format type
format = format.ToUpper();
switch (format)
{
// our user format
case "U":
// get decimals
string decimals = val.ToString("G", CultureInfo.InvariantCulture);
decimals = decimals.Substring(decimals.IndexOf('.') + 1);
// get current culture info
NumberFormatInfo nfi = new CultureInfo(CultureInfo.CurrentCulture.Name).NumberFormat;
// set our separators
nfi.NumberGroupSeparator = ",";
nfi.NumberDecimalSeparator = ".";
// set numebr of decimals
nfi.NumberDecimalDigits = decimals.Length;
// convert value to our format
valueString = val.ToString("N", nfi);
break;
default:
break;
}
return valueString;
}
}
}
class Program
{
static void Main(string[] args)
{
decimal dec = 32445.324777M;
Console.WriteLine(String.Format(new CustomerFormatter(), "{0}", dec));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:G}", dec));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:U}", dec));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:T}", dec));
Console.ReadLine();
}
}
Related
Is it possible in the following line
<td>#String.Format(new CultureInfo("el-GR"), "{0:#,,0.00}", value)</td>
to manipulate the output, so if value is NaN or infinity to return 0?
Thanks in advance for any help.
Regards
I won't judge on the goodness/badness of what you want to do here
From msdn:
Regardless of the format string, if the value of a Single or Double floating-point type is positive infinity, negative infinity, or not a number (NaN), the formatted string is the value of the respective PositiveInfinitySymbol, NegativeInfinitySymbol, or NaNSymbol property specified by the currently applicable NumberFormatInfo object.
So as suggested here you could reconfigure your CultureInfo to have whatever string representation of NaN, PositiveInfinity, NegativeInfinity you want, but note that they are string values, so they won't format to 0,00. You can clearly set their string value to to 0,00 ("0,00" is a legal string value), like this:
public static readonly CultureInfo GreekCultureInfoWithoutNanInfinity0dot00 = CreateCultureInfoWithoutNanInfinity("gr-GR", "0.00");
private static CultureInfo CreateCultureInfoWithoutNanInfinity(string name, string format)
{
var ci = new CultureInfo(name);
string num = (0d).ToString(format, ci);
ci.NumberFormat.PositiveInfinitySymbol = num;
ci.NumberFormat.NegativeInfinitySymbol = num;
ci.NumberFormat.NaNSymbol = num;
return ci;
}
and then use the GreekCultureInfoWithoutNanInfinity0dot00 as the culture when you want to format numbers in Greek format "0.00",
Or perhaps you could directly create a new
public static string FormatNoNaN(IFormatProvider provider, string format, double obj1)
where you handle the "special cases" of NaN, PositiveInfinity, NegativeInfinity.
I'm a bit stuck: I am trying to perform an if statement comparing a label.text that originally has a currency string created from .ToString("C");
and a decimal. I have tried converting the label to decimal since I'm going to be comparing it to a decimal but it keeps triggering an exception:
Input string was not in a correct format.
here is my current code:
if(Convert.ToDecimal(SomeLabel.Text) > 1000.00m) { //DO SOMETHING }
//SomeLabel.Text has a value of $1000.00
//SomeLabel.Text has a value of $1000.00
The dollar sign is the reason for the issue. Yoa are probably using a different currency symbol. You can force it with decimal.Parse/decimal.TryParse(which handles invalid input):
string input = "$1000.00";
decimal decimalValue;
if(decimal.TryParse(input, out decimalValue))
{
// using current CurrencySymbol, same as Convert.ToDecimal
Console.WriteLine("Converted successfully: " + decimalValue);
}
else
{
var usCulture = new CultureInfo("en-US");
if (decimal.TryParse(input, NumberStyles.Currency, usCulture, out decimalValue))
{
// using dollar sign as CurrencySymbol
Console.WriteLine("Converted successfully with CultureInfo(en-US): " + decimalValue);
}
else
{
Console.WriteLine("Could not be parsed to decimal");
}
}
Output:
Converted successfully with CultureInfo(en-US): 1000.00
It's worth noting that NumberFormatInfo.InvariantInfo.CurrencySymbol does not return the dollar sign(what i thought) but ¤.
Convert.ToDecimal uses decimal.Parse explicitly this method uses NumberStyles.Number by default.
This is a composite style which includes AllowDecimalPoint but not AllowCurrencySymbol style even if your CurrentCulture's CurrencySymbol is $ and NumberDecimalSeparator is ..
You can use decimal.parse(String, NumberStyles, IFormatProvider) overlaod that takes these as a parameters like;
string s = "$1000.00";
var d = decimal.Parse(s, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint,
CultureInfo.GetCultureInfo("en-US"));
d will be 1000 after parsing.
As per your example. your string value is $1000.00.
This contains a $ sign that's why the compiler is unable to cast string to decimal.
try removing the unusable character by replace. Or try the method below
Regex digitsOnly = new Regex(#"[0-9.]"); // this regex matches only digits and decimal
string decimalnumberstring = digitsOnly.Replace(SomeLabel.Text, "");// replace all non numbers (except decimal) with empty string
then try to convert the string value like below.
if(Convert.ToDecimal(decimalnumberstring) > 1000.00m) { //DO SOMETHING }
hope it helps....
Take the Dollar Sign off of your text first:
Source: $1000
double result = 0M;
String value = SomeLabel.Text.Substring(1);
if (Double.TryParse(value, out result)) {
//
}
return result;
Try this
if(decimal.Parse(SomeLabel.Text) > 1000.00m)
{
}
I need validate double values. When I TryParse .00 or ,00 return false, but I allow this values must true.
This code not work correctly for this case
model = ,00 or .002 or ,789 or .12 ...
double number;
double.TryParse(model, NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number)
You could loop all supported cultures:
bool parsable = false;
double number = 0;
var supportedCultures = new[] { CultureInfo.InvariantCulture, CultureInfo.CreateSpecificCulture("de-DE") };
foreach(var culture in supportedCultures)
{
parsable = double.TryParse(model, NumberStyles.Any, culture, out number);
if (parsable)
break;
}
or with LINQ:
CultureInfo thisCulture = supportedCultures
.FirstOrDefault(c => double.TryParse(model, NumberStyles.Any, c, out number));
if (thisCulture != null)
{
Console.WriteLine("Number was parsable to " + number);
}
old approach, too error-prone as asawyer has pointed out("1,000,000.00"):
You could replace commas with dots:
bool parsable = double.TryParse(model.Replace(",", "."), NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
When you use NumberFormatInfo.InvariantInfo you are forcing the parser to treat . as the decimal separator and , as the thousands separator. So
double.TryParse(".002", out number)
would return true, but
double.TryParse(",789", out number)
would not.
If you want to support multiple formats you'd have to call TryParse for each format. There's not a way to try and parse a number that would be valid in an format because they could return different value. "123,456" could be interpreted two different ways depending on what culture you were using.
I don't think you can really do this safely. If you manage to get your system to handle both ways of defining "." and "," as the decimal separator and grouping separator, then you run into issues of ambiguity.
For instance, what if "1.001" is passed in? What about "1,001"? How do you know if either one really means "one-thousand-and one" or "one and one-thousandth"?
I need convert a String to a decimal in C#, but this string have different formats.
For example:
"50085"
"500,85"
"500.85"
This should be convert for 500,85 in decimal. Is there is a simplified form to do this convertion using format?
Some cultures use a comma to indicate the floating point. You can test this with the following code on an aspx page:
var x = decimal.Parse("500,85");
Response.Write(x + (decimal)0.15);
This gives the answer 501 when the thread culture has been set to a culture that uses the comma as floating point. You can force this like so:
var x = decimal.Parse("500,85", new NumberFormatInfo() { NumberDecimalSeparator = "," });
While decimal.Parse() is the method you are looking for, you will have to provide a bit more information to it. It will not automatically pick between the 3 formats you give, you will have to tell it which format you are expecting (in the form of an IFormatProvider). Note that even with an IFormatProvider, I don't think "50085" will be properly pulled in.
The only consistent thing I see is that it appears from your examples that you always expect two decimal places of precision. If that is the case, you could strip out all periods and commas and then divide by 100.
Maybe something like:
public decimal? CustomParse(string incomingValue)
{
decimal val;
if (!decimal.TryParse(incomingValue.Replace(",", "").Replace(".", ""), NumberStyles.Number, CultureInfo.InvariantCulture, out val))
return null;
return val / 100;
}
This will work, depending on your culture settings:
string s = "500.85";
decimal d = decimal.Parse(s);
If your culture does not by default allow , instead of . as a decimal point, you will probably need to:
s = s.Replace(',','.');
But will need to check for multiple .'s... this seems to boil down to more of an issue of input sanitization. If you are able to validate and sanitize the input to all conform to a set of rules, the conversion to decimal will be a lot easier.
Try this code below:
string numValue = "500,85";
System.Globalization.CultureInfo culInfo = new System.Globalization.CultureInfo("fr-FR");
decimal decValue;
bool decValid = decimal.TryParse(numValue, System.Globalization.NumberStyles.Number, culInfo.NumberFormat, out decValue);
if (decValid)
{
lblDecNum.Text = Convert.ToString(decValue, culInfo.NumberFormat);
}
Since I am giving a value of 500,85 I will assume that the culture is French and hence the decimal separator is ",". Then decimal.TryParse(numValue, System.Globalization.NumberStyles.Number, culInfo.NumberFormat,out decValue);
will return the value as 500.85 in decValue. Similarly if the user is English US then change the culInfo constructor.
There are numerous ways:
System.Convert.ToDecimal("232.23")
Double.Parse("232.23")
double test;
Double.TryParse("232.23", out test)
Make sure you try and catch...
This is a new feature called Digit Grouping Symbol.
Steps:
Open Region and Language in control panel
Click on Additional setting
On Numbers tab
Set Digit Grouping Symbol as custom setting.
Change comma; replace with (any character as A to Z or {/,}).
Digit Grouping Symbol=e;
Example:
string checkFormate = "123e123";
decimal outPut = 0.0M;
decimal.TryParse(checkFormate, out outPut);
Ans: outPut=123123;
Try This
public decimal AutoParse(string value)
{
if (Convert.ToDecimal("3.3") == ((decimal)3.3))
{
return Convert.ToDecimal(value.Replace(",", "."));
}
else
{
return Convert.ToDecimal(value.Replace(".", ","));
}
}
What is the best way to convert any currency into decimal in C#?
public static decimal returnDecimalFromCurrency(string dataToCheck) {
decimal varValue;
if (!Decimal.TryParse(dataToCheck, NumberStyles.Number | NumberStyles.AllowCurrencySymbol, CultureInfo.CurrentCulture, out varValue)) {
varValue = decimal.MinValue;
}
return varValue;
}
But this one does work for only my current currency. If i pass it EURO currency it won't get converted.
Or the only way is to always check what currency it is and pass proper CultureInfo:
CultureInfo.GetCultureInfo("pl-PL") for zl, or CultureInfo.GetCultureInfo("en-GB") for pounds?
Edit:
Or maybe this is the quick "working" hack?
Decimal.TryParse(dataToCheck.Trim().Replace("zł", "").Replace("€", ""), NumberStyles.Number | NumberStyles.AllowCurrencySymbol, CultureInfo.GetCultureInfo("pl-PL"), out varValue)
But to be honest I don't really like it.
I'd avoid trying to parse the currency with Decimal.TryParse and instead strip off the currency - giving the resultant string to TryParse. Deal with the currency separately - of course, create a function to do this if you need to do it more than once.
Here 's what works for me, I hope it helps you too:
public static decimal Parse(string currencyString)
{
return Parse(currencyString, Thread.CurrentThread.CurrentCulture);
}
public static decimal Parse(string currencyString, CultureInfo culture)
{
currencyString = currencyString.Replace(culture.NumberFormat.CurrencySymbol, System.String.Empty);
var value = Convert.ToDecimal(currencyString, culture);
return value;
}
I solved this same problem with a hybrid approach. The regex to ignore period, comma, parenthesis (in my case I know negatives are in parenthesis), and digits is fairly simple. Just use that to find your symbol, and then build a culture from it. Then parse.
//[^\d\.,()]+ all non-digit, non-period, non-comma, non-parenthesis chars
var regex = new Regex("[^\\d\\.,()]+");
var match = regex.Match(currencyString);
if(match == null){ //throw exception }
var currencySymbol = match.Value;
var cultureInfo = Thread.CurrentThread.CurrentCulture.Clone() as CultureInfo;
cultureInfo.NumberFormat.CurrencySymbol = currencySymbol;
var decimalValue = Decimal.Parse(currencyString, NumberStyles.Currency, cultureInfo);
I ran into this issue today, and I ended up just looping through all of the cultures, and passing each one to the TryParse function.
var myString = "$400.01";
var result = 0.0m;
var culture = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures)
.First(c => decimal.TryParse(myString, System.Globalization.NumberStyles.Currency, c, out value))
// result = 400.01m