Converting masked currency string using Decimal.TryParse - c#

I'm having issue converting this string to a decimal. I tried to follow the documentation here with no luck: Decimal.TryParse Method
string stringVal = "-(3434343434.00)";
NumberStyles style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands |
NumberStyles.AllowParentheses | NumberStyles.AllowLeadingSign;
CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
decimal value;
bool parseSuccess = decimal.TryParse(stringVal, style, culture, out value);
parseSuccess is returning false.

I think a - sign and parenthesis at the same time is not valid. -3434343434.00 is fine as is (3434343434.00) but -(3434343434.00) is not valid. -(3434343434.00) does not really make sense it is using 2 different methods to indicate a negative and as such is a bit redundant.

You have both () and a - sign in your string. This is incorrect and is why your string doesn't parse.
See the documentations for AllowParentheses:
AllowParentheses: Indicates that the numeric string can have one pair of parentheses enclosing the number. The parentheses indicate that the string to be parsed represents a negative number.

Related

Would there be a better way of doing this?

post.Min.ToString("0.00").Replace(",", ".").Replace(".00", string.Empty)
post.Min is a double such as 12,34 or 12,00. Expected output is 12.34 or 12.
I basically want to replace the comma by a point, and cut the .00 part if any.
I am asking because I couldn't find anything, or because I don't exactly know what to search. This has an high change of being a duplicate, I simply can't find it. Please let me know.
The simplest solution would appear to be to use CultureInfo.InvariantCulture, and I reject the suggestion that this is any more complicated than using a series of replaces as you demonstrated in your question.
post.Min.ToString("0.##", CultureInfo.InvariantCulture);
# is the digit placeholder, described as the docs like this:
Replaces the "#" symbol with the corresponding digit if one is present; otherwise, no digit appears in the result string.
Try it online
If you use this in a lot of places, and that's why you want to keep it simple, you could make an extension method:
public static class MyExtensions
{
public static string ToHappyString(this double value)
{
return value.ToString("0.##", CultureInfo.InvariantCulture);
}
}
And then you just have to call .ToHappyString() wherever you use it. For example, post.Min.ToHappyString()
You can use .ToString("0.##").
like,
// Considered german culture; May be this is your current culture
CultureInfo culture = new CultureInfo("de");
double number1 = Double.Parse("12,34", culture);
double number2 = Double.Parse("12,00", culture);
Console.WriteLine(number1.ToString("0.##"));
Console.WriteLine(number2.ToString("0.##"));
Output:
12.34
12
.Net fiddle
Checkout the ToString overloads article on MSDN about examples of the N format. This is also covered in the Standard Numeric Format Strings article.
Relevant examples:
// Formatting of 1054.32179:
// N: 1,054.32
// N0: 1,054
// N1: 1,054.3
// N2: 1,054.32
// N3: 1,054.322
For the dot instead of comma to do it properly, in combination with N0 use:
System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
customCulture.NumberFormat.NumberDecimalSeparator = ".";
System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;
double.ToString("0.##") to consider decimal places only if not .00 and you can create your own Number Format without using Culture:
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ".";
post.Min.ToString("0.##", nfi);

How do I parse a string (with commas and the sign at the end, ie. "3,246,928-") in C#?

I've got a string that looks like
3,246,928-
And I'd like to turn this into a C# double.
Is there a preset format that will parse this as a negative double for me?
I tried
double parsedValue = Double.Parse("3,246,928-");
And get an Exception "Input string was not in a correct format."
Is there a way to do this without manually checking for a negative symbol at the end, stripping it out, stripping the commas, etc..?
You can use the NumberStyles enum to indicate which styles are permitted in the input string:
var input = "3,246,928-";
var res = Double.Parse(input, NumberStyles.AllowThousands | NumberStyles.AllowTrailingSign);
Console.WriteLine(res); // -3246928
It may also be a good idea to use TryParse instead:
var input = "3,246,928-";
if (Double.TryParse(input, NumberStyles.AllowThousands | NumberStyles.AllowTrailingSign, null, out var result))
Console.WriteLine(result); // -3246928

Why does TypeConverter not obey Cultures?

I have a CSV Reader, so I have a generic casting method. It it, I do this:
try
{
TypeConverter converter = TypeDescriptor.GetConverter(type);
if (converter != null)
{
result = converter.ConvertFromString(null, culture, value);
return true;
}
result = type.GetDefault();
return true;
}
catch
{
result = type.GetDefault();
return false;
}
However if I pass:
type: int
value: "123.024"
culture: de-DE
The converter always fails and returns 0, instead of correctly treating . as a thousands separator.
Anyone know how to get it to work?
Because the Int32Converter calls Int32.Parse internally, and Int32.Parse does not support thousands separators in the string passed to it.
More specifically, Int32.Parse format described below:
The s parameter contains a number of the form:
[ws][sign]digits[ws]
Items in square brackets ([ and ]) are optional. The following table
describes each element. Element Description
ws Optional white space.
sign An optional sign
digits A sequence of digits ranging from 0 to
9.
The s parameter is interpreted using the NumberStyles.Integer style. In addition to decimal digits, only leading and trailing spaces
together with a leading sign are allowed. To explicitly define the
style elements that can be present in s, use either the
Int32.Parse(String, NumberStyles) or the
Int32.Parse(String, NumberStyles, IFormatProvider) method.
The s parameter is parsed using the formatting information in a NumberFormatInfo object initialized for the current system culture.
For more information, see CurrentInfo. To parse a string using the
formatting information of some other culture, use the
Int32.Parse(String, NumberStyles, IFormatProvider) method.
If you were calling Int32.Parse directly, you could just call the overload that accepts a NumberStyles enum and create a composite value with the flags you want. E.g:
Int32.Parse(value, NumberStyles.Integer | NumberStyles.AllowThousands);
However, neither GetConverter() nor Int32Converter have any means of overriding the default NumberStyles of Int32.Parse, so you will either need a special case for ints or you will have to ensure that the strings passed to this function do not contain thousands separators.

Failed to Parse string to float in C#

I want to convert the following string to "5,28" to float number, I used this code but I got false result. Also I'm setting the device language to french.
Is there something I'm missing? I have tried to convert the string to different culture like CultureInfo("en-US") but still did not work.
bool result = float.TryParse("5,28", NumberStyles.Float,
CultureInfo.InvariantCulture.NumberFormat, out number);
InvariantCulture uses . as a NumberDecimalSeparator not ,
Since you forced to use Float style, this style includes only AllowDecimalPoint in a separator styles, your method thinks this , is a decimal separator but InvariantCulture does not use it. That's why you get exception.
There are a few things you can do. One option can be Clone an InvariantCulture, set NumberDecimalSeparator property to , and use that cloned culture in your TryParse method.
float f;
var clone = (CultureInfo)CultureInfo.InvariantCulture.Clone();
clone.NumberFormat.NumberDecimalSeparator = ",";
var result = float.TryParse("5,28", NumberStyles.Float, clone, out f); // true
Or you can use a culture that already has , as a NumberDecimalSeparator like tr-TR culture.1
float f;
var result = float.TryParse("5,28", NumberStyles.Float,
CultureInfo.GetCultureInfo("tr-TR"), out f); // true
1:Since I'm from Turkey :)
The reason that the value 5,28 does not parse is that invariant culture uses decimal dot ., not decimal comma.
To solve this problem you could either replace comma with a dot, like this
bool result=float.TryParse(
"5.28"
, NumberStyles.Float
, CultureInfo.InvariantCulture.NumberFormat
, out number);
or replace CultureInfo.InvariantCulture for a culture that uses comma in place of a dot:
bool result=float.TryParse(
"6,78"
, NumberStyles.Float
, new CultureInfo("de-DE").NumberFormat
, out number);
Demo.

Parse a Number from Exponential Notation

I need to parse the string "1.2345E-02" (a number expressed in exponential notation) to a decimal data type, but Decimal.Parse("1.2345E-02") simply throws an error
It is a floating point number, you have to tell it that:
decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
It works if you specify NumberStyles.Float:
decimal x = decimal.Parse("1.2345E-02", NumberStyles.Float);
Console.WriteLine(x); // Prints 0.012345
I'm not entirely sure why this isn't supported by default - the default is to use NumberStyles.Number, which uses the AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowTrailingSign, AllowDecimalPoint, and AllowThousands styles. Possibly it's performance-related; specifying an exponent is relatively rare, I suppose.
In addition to specifying the NumberStyles I would recommend that you use the decimal.TryParse function such as:
decimal result;
if( !decimal.TryParse("1.2345E-02", NumberStyles.Any, CultureInfo.InvariantCulture, out result) )
{
// do something in case it fails?
}
As an alternative to NumberStyles.Any you could use a specific set if you're certain of your formats. e.g:
NumberStyles.AllowExponent | NumberStyles.Float
decimal d = Decimal.Parse("1.2345E-02", System.Globalization.NumberStyles.Float);
Be cautious about the selected answer: there is a subtility specifying System.Globalization.NumberStyles.Float in Decimal.Parse which could lead to a System.FormatException because your system might be awaiting a number formated with ',' instead of '.'
For instance, in french notation, "1.2345E-02" is invalid, you have to convert it to "1,2345E-02" first.
In conclusion, use something along the lines of:
Decimal.Parse(valueString.Replace('.',','), System.Globalization.NumberStyles.Float);
The default NumberStyle for decimal.Parse(String) is NumberStyles.Number, so if you just want to add the functionality to allow exponents, then you can do a bitwise OR to include NumberStyles.AllowExponent.
decimal d = decimal
.Parse("1.2345E-02", NumberStyles.Number | NumberStyles.AllowExponent);
I've found that passing in NumberStyles.Float, in some cases, changes the rules by which the string is processed and results in a different output from NumberStyles.Number (the default rules used by decimal.Parse).
For example, the following code will generate a FormatException in my machine:
CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5", NumberStyles.Float, culture); // FormatException thrown here
I'd recommend using the input NumberStyles.Number | NumberStyles.AllowExponent, as this will allow exponential numbers and will still process the string under the decimal rules.
CultureInfo culture = new CultureInfo("");
culture.NumberFormat.NumberDecimalDigits = 2;
culture.NumberFormat.NumberDecimalSeparator = ".";
culture.NumberFormat.NumberGroupSeparator = ",";
Decimal.Parse("1,234.5",NumberStyles.Number | NumberStyles.AllowExponent, culture); // Does not generate a FormatException
To answer the poster's question, the right answer should instead be:
decimal x = decimal.Parse("1.2345E-02", NumberStyles.Number | NumberStyles.AllowExponent);
Console.WriteLine(x);
Warning about using NumberStyles.Any:
"6.33E+03" converts to 6330 as expected. In German, decimal points are represented by commas, but 6,33E+03 converts to 633000! This is a problem for my customers, as the culture that generates the data is not known and may be different than the culture that is operating on the data. In my case, I always have scientific notation, so I can always replace comma to decimal point before parsing, but if you are working with arbitrary numbers, like pretty-formatted numbers like 1,234,567 then that approach doesn't work.
You don't need to replace the dots (respectively the commas) just specify the input IFormatProvider:
float d = Single.Parse("1.27315", System.Globalization.NumberStyles.Float, new CultureInfo("en-US"));
float d = Single.Parse("1,27315", System.Globalization.NumberStyles.Float, new CultureInfo("de-DE"));
If you want to check and convert the exponent value use this
string val = "1.2345E-02";
double dummy;
bool hasExponential = (val.Contains("E") || val.Contains("e")) && double.TryParse(val, out dummy);
if (hasExponential)
{
decimal d = decimal.Parse(val, NumberStyles.Float);
}
Hope this helps someone.

Categories