This question already has an answer here:
Why does NumberStyles.AllowThousands work for int.Parse but not for double.Parse in this example?
(1 answer)
Closed 3 years ago.
having a string that's looking like "1.900,00". How could I format this string to a decimal of 1900?
Using:
decimal.TryParse("1.900,00", out var vkp)
Will give me a false result. How can I handle this?
Here's a URL to the online C# compiler: https://dotnetfiddle.net/B5EyyC
You could try using a CultureInfo that uses '.' as thousands separator and ',' as decimal separator (for example "de-DE"). And also the appropriate NumberStyles.
string input = "1.900,00";
decimal.TryParse(input,
NumberStyles.AllowThousands | NumberStyles.AllowDecimalPoint,
CultureInfo.GetCultureInfo("de-DE"), out var output);
You can even create your very own instance of NumberFormatInfo where you can specify the NumberDecimalSeparator and NumberGroupSeparator any way you like.
try this:
string s = "1.900,00";
s = s.Substring(0, s.LastIndexOf(",")).Replace(".", "") + "." + s.Substring(s.LastIndexOf(",") + 1);
decimal vkp = 0;
decimal.TryParse(s, out vkp);
I guess you could try the following:
string number = "1.900,00";
decimal.TryParse(number.Split(",")[0].Replace(".",""), out var vkp);
In case you need a more foolproof checking you could try making a function that checks whether or not the string contains a ',' character.
public static bool ToDouble(string s, out double d)
{
if (s.Contains(","))
return double.TryParse(s.Split(",")[0].Replace(".", ""), out d);
return double.TryParse(s, out d);
}
Also you can get the decimal separator of you localization settings via:
string dsep = CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator
Decimal.TryParse doc
//using System.Globalization;
NumberStyles style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
CultureInfo culture = CultureInfo.CreateSpecificCulture("es-ES");
Decimal vkp;
Decimal.TryParse("1.900,00", style, culture, out vkp);
Related
The problem:
Let's assume you are using a dot "." as a decimal separator in your regional setting and have coded a string with a comma.
string str = "2,5";
What happens when you decimal.TryParse(str, out somevariable); it?
somevariable will assume 0.
What can you do to solve it?
1-
You can
decimal.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out somevariable);
And it will return 25, and not 2.5 which is wrong.
2-
You can
decimal.TryParse(str.Replace(",","."), out num);
And it will return the proper value, BUT, if the user uses "," as a decimal separator it will not work.
Possible solution that I can't make it work:
Get the user decimal separator in regional settings:
char sepdec = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
And make somehow the replace from ",",sepdec , that way it would stay a comma if its a comma, and replace by an actual dot if the user uses dots.
Hints?
Edit: Many users posted useful information, lately, using the arguments NumberStyles.Any, CultureInfo.GetCultureInfo("pt-PT") on a tryParse wouldn't work if your separator is set to "," So it pretty much doesnt fullfill the premise of making a tryparse "universal".
I'll work around this, if anyone has more hints you'r welcome
I know the thread is a little bit older, but I try to provide an answer.
I use regular expression to determine the used number format in the string.
The regex also matches numbers without decimal separators ("12345").
var numberString = "1,234.56"; // en
// var numberString = "1.234,56"; // de
var cultureInfo = CultureInfo.InvariantCulture;
// if the first regex matches, the number string is in us culture
if (Regex.IsMatch(numberString, #"^(:?[\d,]+\.)*\d+$"))
{
cultureInfo = new CultureInfo("en-US");
}
// if the second regex matches, the number string is in de culture
else if (Regex.IsMatch(numberString, #"^(:?[\d.]+,)*\d+$"))
{
cultureInfo = new CultureInfo("de-DE");
}
NumberStyles styles = NumberStyles.Number;
bool isDouble = double.TryParse(numberString, styles, cultureInfo, out number);
HTH
Thomas
I just want to say that HTH Thomas solution worked really well in my project, except for when trying to parse negative decimal numbers with commas. One solution to this, which is probably not optimized because I don't fully understand the regex Ismatch code, but that works is adding the possibility of finding a "-" before the number in the ifs statements, like this:
var cultureInfo = CultureInfo.InvariantCulture;
if (Regex.IsMatch(equation.inputFieldsTexts[i], #"^(:?[\d,]+\.)*\d+$") || Regex.IsMatch(equation.inputFieldsTexts[i], #"^(:?[-\d,]+\.)*\d+$"))
{
cultureInfo = new CultureInfo("en-US");
}
// if the second regex matches, the number string is in DE culture
if (Regex.IsMatch(equation.inputFieldsTexts[i], #"^(:?[\d.]+,)*\d+$") || Regex.IsMatch(equation.inputFieldsTexts[i], #"^(:?[-\d.]+,)*\d+$"))
{
cultureInfo = new CultureInfo("de-DE");
}
The solution I use is to simply show the user what the parsed value is.
I have a custom TextBox control which verifies the input when the control loses focus and such. If the control expects a floating point value (which is a property), then it will try to parse the value entered. If the TryParse succeeds, I display the out value in the control's text.
This way, when a user enters 12.3 the value might change to 123 because in the current culture 12,3 is expected. It's then up to them to decide to correct this.
How about this method:
clean the string from anything else than numbers, dot, comma and negative sign
take the last index of dot or comma
split the clean string and remove all thousands separators from the first part
convert both parts to integer
change the sign of the second part if necessary
add the first part with the second part divided by decimal places
public static bool TryParseDoubleUniversal(this string s, out double result) {
result = 0.0;
if (string.IsNullOrEmpty(s)) return false;
var clean = new string(s.Where(x => char.IsDigit(x) || x == '.' || x == ',' || x == '-').ToArray());
var iOfSep = clean.LastIndexOfAny(new[] { ',', '.' });
var partA = clean.Substring(0, iOfSep).Replace(",", string.Empty).Replace(".", string.Empty);
var partB = clean.Substring(iOfSep + 1);
if (!int.TryParse(partA, out var intA)) return false;
if (!int.TryParse(partB, out var intB)) return false;
if (intA < 0) intB *= -1;
var dp = double.Parse("1".PadRight(partB.Length + 1, '0'));
result = intA + intB / dp;
return true;
}
The question is old but since it was my first hit on Google. And the approach in How to change symbol for decimal point in double.ToString()? seems to be a valid solution you can use the NumberFormatInfo to set the decimal separator like this:
string value = "3,2";
NumberFormatInfo nfi = new NumberFormatInfo();
nfi.NumberDecimalSeparator = ",";
decimal.TryParse(value, NumberStyles.Any, nfi, out decimal dec);
I found a solution, I'm a beginner on this regional and comma-dots theme so if you have comments to improve the understanding of this please be welcome.
We start of by getting what decimal separator the user has set in his regional options outside before the Form{InitializeComponent();} (I want a universal variable that will allow me to correct the code)
char sepdec = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
In the tryParse, to get it to behave universally we will read the dots and commas in the string, and turn them into the decimal separator we defined as sepdec
decimal.TryParse(str.Replace(",",sepdec.ToString()).Replace(".",sepdec.ToString()), out somevariable);
I hope this helps, please comment improvement suggestions!
In Android Xamarin, I ran into the same issue several times. Some solutions worked until the Android got upgraded into a new version, then the problem came out again. So I came with an universal solution, which works fine. I read the numeric input as text, then parse it into decimal with a custom parser.
The custom parser is returning 0 when parsing into decimal is not possible. It does allow input text containing decimal number with either comma or dot, with no group separators:
public static decimal ParseTextToDecimal(string decimalText)
{
if (decimalText == String.Empty) return 0;
string temp = decimalText.Replace(',', '.');
var decText = temp.Split('.');
if (!Int32.TryParse(decText[0], out int integerPart)) return 0;
if (decText.Length == 1) return integerPart;
if (decText.Length == 2)
{
if (!Int32.TryParse(decText[1], out int decimalPart)) return 0;
decimal powerOfTen = 10m;
for (int i = 1; i < decText[1].Length; i++) powerOfTen *= 10;
return integerPart + decimalPart / powerOfTen;
}
return 0; // there were two or more decimal separators, which is a clear invalid input
}
Is there a way to convert a string to decimal in C# but ignoring trailing "garbage"? i.e. like PHP's floatval() or C strtod() ?
e.g.
Convert string "2974.23abcdefs" to decimal 2974.23
As others have mentioned, there is no exact, like for like, replacement for what you can do in PHP and I think, for good reason. In the scenario of a web application, I'm not really sure that if I were accepting a decimal with garbage at the end, I'd actually want to consider that as valid data but this is just my opinion.
What you can do is define a regular expression that would capture the decimal and recognise that this is happening. I find this much safer and reliable.
Obviously, the regular expression can be improved but this is a simple example for you: -
var match = Regex.Match("2974.23abcdefs", "^([0-9]+\\.[0-9]+)(.+)?$");
if (match.Success)
{
// See the also the link below for using Decimal.TryParse
Console.WriteLine(Convert.ToDecimal(match.Groups[1].Value));
}
See https://msdn.microsoft.com/en-us/library/system.decimal.tryparse%28v=vs.110%29.aspx for my preferred way to convert to a decimal. This would ensure that you are coping with the output of the regular expression for how Decimal is comprised
For more information on regular expressions, see https://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex%28v=vs.110%29.aspx
This works but only takes care of digits and the current culture's decimal-separator:
string input = "2974.23abcdefs";
decimal d;
char decSep = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator[0]; // there is no culture with a decimal-separator that has more than one letter so this isn't harmful
if(!string.IsNullOrEmpty(input) && Char.IsDigit(input[0]))
{
string number = string.Concat(input.TakeWhile(c => Char.IsDigit(c) || decSep == c));
bool validDecimal = decimal.TryParse(number, out d);
Console.WriteLine("Valid? {0} Parsed to: {1}", validDecimal, d);
}
Since we are using , as decimal separator here in germany i get a different result than people who use . as separator. You get 2974.23 and i get 2974.
As a first, second, third try, this should go:
static double Parse(string str, IFormatProvider provider = null)
{
if (str == string.Empty)
{
return 0;
}
if (provider == null)
{
provider = CultureInfo.CurrentCulture;
}
NumberFormatInfo nfi = NumberFormatInfo.GetInstance(provider);
// [ws][sign][integral-digits[,]]integral-digits[.[fractional-digits]][E[sign]exponential-digits][ws]
string ws = #"\s*";
string sign = #"(" + Regex.Escape(nfi.PositiveSign) + "|" + Regex.Escape(nfi.NegativeSign) + ")?";
string integralDigits1 = "([0-9](" + Regex.Escape(nfi.NumberGroupSeparator) + ")*)*";
string integralDigits2 = "[0-9]+";
string fractionalDigits = "(" + Regex.Escape(nfi.NumberDecimalSeparator) + "[0-9]*)?";
string exponentialDigits = "([Ee]" + sign + "[0-9]+)?";
var rx = new Regex(ws + sign + integralDigits1 + integralDigits2 + fractionalDigits + exponentialDigits);
string match = rx.Match(str).ToString();
if (match == string.Empty)
{
return 0;
}
return double.Parse(match, provider);
}
Note that the composed regex is very complex, because there are various "parts" in a full double that has been written to a string.
From MSDN:
[ws][sign][integral-digits[,]]integral-digits[.[fractional-digits]][E[sign]exponential-digits][ws]
Still some numbers will crash this function, if they are too much big. So passing new string('9', 1000) will make the double.Parse throw an exception.
Use it like:
double num = Parse(" +1,0.1234E+12abcdefgh", CultureInfo.InvariantCulture);
or
double num = Parse(" +1,,,,,0.1234E+12abcdefgh");
(if you don't need to configure the culture, will use the CultureInfo.CurrentCulture)
There are many ways to do so. I suggest using a Regex first, and then decimal.TryParse().
This is a regex that grabs a floating point number at the begin of the string, like -123.43 or just 1234.56 or 123456:
^([+-][0-9]+\.?[0-9]*).*$
Putting this into C# looks like this:
// Step 1: Getting some input
String input = "123.4533wefwe";
// Step 2: Get rid of the trail
Regex r = new Regex(#"^([+-][0-9]+\.?[0-9]*).*$", RegexOptions.IgnoreCase);
MatchCollection matches = r.Matches(input);
if (matches.Count > 0) {
Match match = matches[0];
GroupCollection groups = match.Groups;
// Step 3: create a real decimal from the string
decimal i;
NumberStyles style;
CultureInfo culture;
style = NumberStyles.Number;
culture = CultureInfo.CreateSpecificCulture("en-GB");
String matchedNumber = groups[1].Value;
if (decimal.TryParse(matchedNumber, style, culture, out i)) {
// Step 4: giving back the result:
Console.WriteLine("Parsed decimal: " + i);
}
}
The output of this is:
Parsed decimal: 123.4533
Remark: All this seems to become a bigger problem if you would like to parse real floating point number literals that include exponential notation. Then, severals stages of casting would be necessary.
I already searched for my problem but I wasn't successfully and that's the reason I'm here.
All I want to do is reading a string like "3.14" and convert it to double.
Enough said... here is my code:
using System;
namespace GlazerCalcApplication
{
class MainClass
{
public static void Main (string[] args)
{
string heightString;
double height;
heightString = Console.ReadLine();
height = Convert.ToDouble(heightString);
Console.WriteLine(height);
}
}
}
Output:
3.14
314
Press any key to continue...
Why is my double value not 3.14?
Instead of Convert.ToDouble() I also tried it with double.Parse() but I received the same behaviour. Reading strings like 3,14 is no problem.
Maybe I should also mention that I use MonoDevelop and a linux OS.
Thanks in advance.
Try specifying the culture as Invariant:
height = Convert.ToDouble(heightString,CultureInfo.InvariantCulture);
It seems the decimal seperator of your culture is comma instead of dot therefore dot is truncated after conversion.
Convert.ToDouble(string) uses Double.Parse(string, CultureInfo.CurrentCulture) method explicitly.
Here how it's implemented;
public static double ToDouble(String value) {
if (value == null)
return 0;
return Double.Parse(value, CultureInfo.CurrentCulture);
}
It is likely your CurrentCulture's NumberFormatInfo.NumberDecimalSeparator property is not . (dot). That's why you can't parse a string with . as a date seperator.
Example in LINQPad;
CultureInfo c = new CultureInfo("de-DE");
c.NumberFormat.NumberDecimalSeparator.Dump(); // Prints ,
As a solution, you can create a new reference of your CurrentCulture and assing it's NumberDecimalSeparator property to . like;
double height;
CultureInfo c = new CultureInfo("de-DE");
c.NumberFormat.NumberDecimalSeparator = ".";
height = Convert.ToDouble("3.14", c);
Judging by the result I take it you are in a culture zone where comma is the normal decimal separator.
Also, I take it that you want both dot and comma to be used for decimal separation.
If not, the below is not the proper solution.
The fastest solution for using both would be
height = Convert.ToDouble(heightString.Replace('.', ',');
This would mean that both dots and comma's are used as comma and thus parsed as a decimal separator.
If you only want to use a dot as separator, you can use invariantculture or a specific numberformatinfo. Invariant culture is already shown in the other posts. numberformat info example:
var nfi = new NumberFormatInfo { NumberDecimalSeparator = "." };
height = double.Parse(heightString,nfi);
For completeness, the example below shows both using numberformatinfo for setting the dot as decimal separator, as well as replacing comma with dots, so both characters are used for decimals
var nfi = new NumberFormatInfo { NumberDecimalSeparator = "." };
height = double.Parse(heightString.Replace(',', '.'),nfi);
Different .Net cultures (countries) have different decimal separators.
If you expect input values to be in some specific format - either use some particular culture or InvariantCulture. Also consider using double.Parse as it geve more flexibility on parsing the values than generic Convert.ToDouble.
var d = double.Parse(heightString, CultureInfo.InvariantCulture);
If you expect user to enter value in local format - your code is fine, but either your expectation of "local format" is wrong, or "current culture" set incorrectly.
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.
I feed a textbox a string value showing me a balance that need to be formatted like this:
###,###,###,##0.00
I could use the value.ToString("c"), but this would put the currency sign in front of it.
Any idea how I would manipulate the string before feeding the textbox to achieve the above formatting?
I tried this, without success:
String.Format("###,###,###,##0.00", currentBalance);
Many Thanks,
If the currency formatting gives you exactly what you want, clone a NumberFormatInfo with and set the CurrencySymbol property to "". You should check that it handles negative numbers in the way that you want as well, of course.
For example:
using System;
using System.Globalization;
class Test
{
static void Main()
{
NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
nfi = (NumberFormatInfo) nfi.Clone();
Console.WriteLine(string.Format(nfi, "{0:c}", 123.45m));
nfi.CurrencySymbol = "";
Console.WriteLine(string.Format(nfi, "{0:c}", 123.45m));
}
}
The other option is to use a custom numeric format string of course - it depends whether you really want to mirror exactly how a currency would look, just without the symbol, or control the exact positioning of digits.
string forDisplay = currentBalance.ToString("N2");
Have you tried:
currentBalance.ToString("#,##0.00");
This is the long-hand equivalent of:
currentBalance.ToString("N2");
string result=string.Format("{0:N2}", value); //For result like ### ### ##.##
You can do this with the group separator and the section separator, like this:
currentBalance.ToString("#,0.00;(#,0.00)");
This does not account for culture variances like the answer from #JonSkeet would, but this does mimic decimal place, rounding, thousands separation, and negative number handling that en-US culture currency format produces using a single custom format string.
.NET Fiddle Demo
var result = currentBalance.ToString("C").Replace(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol, "");
CultureInfo cultureInfo = new CultureInfo("en-US");
cultureInfo.NumberFormat.CurrencySymbol = "Rs.";
Thread.CurrentThread.CurrentCulture = cultureInfo;
decimal devimalValue = 3.45M;
this.Text = devimalValue.ToString("C2"); //Rs.3.45
This may be overkill, but it rounds, formats...
#helper TwoDecimalPlaces(decimal? val)
{
decimal x = 0;
decimal y = 0;
string clas = "text-danger";
if (val.HasValue)
{
x = (decimal)val;
if (val > 0)
{
clas = "";
}
}
y = System.Math.Round(x, 2);
IFormatProvider formatProvider = new System.Globalization.CultureInfo(string.Empty);
<span class="#clas">#string.Format("{0:N2}", y)</span>
}
This simple solution works for me with US currency.
If not needing international currency support use this and replace the $ with the currency symbol(s) to be removed:
// for USD
string result = currentBalance.ToString("C").Replace("$", "")
or
// for EUR
string result = currentBalance.ToString("C").Replace("€", "")