validating decimal numbers in culture - c#

can anyone please explain why, in 'en-GB' culture formatting, the decimal.Parse() function would return 15 for 1,5?
I see the same result for 'de' culture when inputting 1.5, the return is 15.
I know this is programmatically correct, I'm just trying to understand why this is correct. I've Googled this and come up with nothing :(
i'm trying to validate user inputs against culture, and populating the input field with a 0 when the parse fails, but populating the field with 15 when they've entered 1,5 doesn't feel right, i feel like it should be "failing" when a 1,5 is entered for english formating, instead of returning 15.
try {
validateSource.Text = decimal.Parse(validateThis, NumberStyles.Number, UserCulture).ToString();
} catch {
validateSource.Text = "0";
}

, is the NumberGroupSeparator in the UK culture. There's no validation in terms of how many digits are in each group. So "1,2,3" would be parsed as 123, even though that's not how we'd normally expect it to be written.
While it would make sense for parsing to check the NumberGroupSizes property, it simply doesn't.
You could emulate this check in a very primitive fashion by formatting the result of parsing, and see whether that's equal to the original input:
decimal value;
if (decimal.TryParse(text, NumberStyles.Number, culture, out value))
{
if (text != value.ToString("N", culture))
{
// Okay, looks like something's up... report an error
}
}
Of course, that would the stop someone from entering "1234.56" as it would expect "1,234.56"... you might want to check multiple pattern formats, e.g. "N" and "G", to see whether it matches any of them.

Related

numeric format strings #,#0.00 vs #,0.00

I tried to figure out the basics of these numeric string formatters. So I think I understand the basics but there is one thing I'm not sure about
So, for example
#,##0.00
It turns out that it produces identical results as
#,#0.00
or
#,0.00
#,#########0.00
So my question is, why are people using the #,## so often (I see it a lot when googling)
Maybe I missed something.
You can try it out yourself here and put the following inside that main function
double value = 1234.67890;
Console.WriteLine(value.ToString("#,0.00"));
Console.WriteLine(value.ToString("#,#0.00"));
Console.WriteLine(value.ToString("#,##0.00"));
Console.WriteLine(value.ToString("#,########0.00"));
Probably because Microsoft uses the same format specifier in their documentation, including the page you linked. It's not too hard to figure out why; #,##0.00 more clearly states the programmer's intent: three-digit groups separated by commas.
What happens?
The following function is called:
public string ToString(string? format)
{
return Number.FormatDouble(m_value, format, NumberFormatInfo.CurrentInfo);
}
It is important to realize that the format is used to format the string, but your formats happen to give the same result.
Examples:
value.ToString("#,#") // 1,235
value.ToString("0,0") // 1,235
value.ToString("#") // 1235
value.ToString("0") // 1235
value.ToString("#.#")) // 1234.7
value.ToString("#.##") // 1234.68
value.ToString("#.###") // 1234.679
value.ToString("#.#####") // 1234.6789
value.ToString("#.######") // = value.ToString("#.#######") = 1234.6789
We see that
it doesn't matter whether you put #, 0, or any other digit for that matter
One occurrence means: any arbitrary large number
double value = 123467890;
Console.WriteLine(value.ToString("#")); // Prints the full number
, and . however, are treated different for double
After a dot or comma, it will only show the amount of character that are provided (or less: as for #.######).
At this point it's clear that it has to do with the programmer's intent. If you want to display the number as 1,234.68 or 1234.67890, you would format it as
"#,###.##" or "#,#.##" // 1,234.68
"####.#####" or "#.#####" // 1234.67890

double.Parse not working on DataGridView Cell Value

I've got a DataGridView that I'd like to apply formatting changes to based on cell values. I'd like to do it using the original DataTable source but unfortunately building the source required removing and reorienting certain rows so the indexes are off. As such, I'm trying to do it using
double.TryParse(DataGridView
.Rows[index]
.Cells["ColumnName"]
.FormattedValue.ToString(), out dbl);
however, it never recognizes the value as a double. I've verified the output using MessageBox'es and that there are no leading or trailing spaces that might cause issue by adding "-" to both sides of the MessageBox string. I'm clueless as to why it will always regard the value as not a double even on values that are clearly parseable as doubles.
Edit : I'm not sure what changed over the weekend but I came in, fixed a different bug unrelated to this and now the bold is working using .Value rather than .FormattedValue as suggested by gh0st below.
double.TryParse() will parse the string in a culture dependant way. It is very likely that your current culture expects the decimal point to be some other symbol, like the comma or something.
For instance, on my machine, I can force it to fail the parsing by passing a different culture, like this:
double.TryParse("1.2",
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.GetCultureInfo("fr-FR"),
out dbl); // returns false
If this is what is happening to you, then either you find a way to change the current culture, or perhaps you can do the parsing this way instead to ensure consistent results:
double.TryParse("1.2",
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out dbl); // returns true
(*) Note: the NumberStyles.Float | NumberStyles.AllowThousands values can be adjusted as you like. I just set it to that in this example, because that is the default that the TryParse method uses when using the overload with less parameters.

Responding to Invalid TextBox Entries

Users enter numbers into textboxes, and the values are displayed on a graph. I want to be sure users type numbers in the textboxes, so I tried:
double xValue;
double yValue;
Double.TryParse(xTextBox.Text, out xValue);
Double.TryParse(yTextBox.Text, out yValue);
chart1.Series["data1"].Points.AddXY(xValue, yValue);
Double.TryParse() validates whether users type numbers, but I'm not sure how to code for when users don't type numbers. Double.TryParse() will assign xValue or yValue a 0--but I also want users to be able to enter 0 values so I cannot just code something like
if (xValue!=0 && yValue!=0)...
Something like
if (xTextBox.Text!="0") {
Double.TryParse(xTextBox.Text, out xValue);
}
seems like awkward code that will just get more. What's the standard way to make sure users entered a double?
TryParse returns a boolean which is true if the conversion succeeded, and false if it failed.
if (!Double.TryParse(xTextBox.Text, out xValue)) {
// handle a failure to convert here
}
Double.TryParse returns a boolean if parsing was a success, and you should check the results of Double.TryParse not the xValue and yValue.
First of all you need to take into consideration the user locale settings. For example in USA the decimal separator is . and in my country it is ,. So you need to specify the format when user enters double values or replace , with . and then parse values with Invariant culture.
Second Double.TryParse method returns bool value which you can use to check whether the entered value is successfully parsed as double.

Using substring to validate last 4 digits of a textbox C# (vb6 Right function)

I am attempting to validate a date text box to make sure the correct date format was entered.
I converted this vb6 code:
If (IsDate(txtBirthDate)) And (IsNumeric(Right(txtBirthDate, 4))))
into this C# code -
int output = 1;
DateTime output2;
if ((! DateTime.TryParse(txtBirthDate.Text, out output2)) & (!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 5)), out output)))
{
MessageBox.Show("error")
}
What I am attempting to do is make sure that the last 4 digits of the date text box are numeric (the year - i.e 1990 in 05/10/1990) and if it is not a number, then show error. Although I cannot verify everything is numeric due to the "/" in the date format.
The code does not show an error and builds. But when I debug the application I receive an error. The error states:
Index and length must refer to a location within the string.
Parameter name: length.
Any ideas on how to accomplish this?
To check if a date is in a specific format, use DateTime.TryParseExact():
if (!DateTime.TryParseExact(output , "d/M/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out output2))
{
MessageBox.Show("error")
}
EDIT: Change the format according to your needs:
"d/M/yyyy" for UK and "M/d/yyyy" for US
Edit: It sounds like the cause of your error is your string is too short. Test the string length before you test the last 4 characters.
Three other issues:
The And operator in C# is &&. You are using & which is a bitwise operator.
To check the last four characters of the string, you should use .Length - 4, not 5.
You are negating the return values in your C#, but not in your VB. To match the VB, omit the !. But, it looks like that's not what you are actually trying to do. It looks like you want to show an error message if either the string is not parsable as a date or the year portion is not 4 digits. In that case use a an or comparison (||):
if (!DateTime.TryParse(txtBirthDate.Text, out output2) ||
txtBirthDate.Text.Length < 4 ||
!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 4)), out output))
{
MessageBox.Show("error")
}
Keep in mind that yyyy/M/d is also parsable as a Date, contains a 4-digit year, but will fail your test. Is that what you want?
From the error provided it looks like you just need a length check to make sure you can process the code. You should also use && instead of & (or in this case probably ||) to ensure that the boolean expressions stop executing once a true state has been encountered.
if (txtBirthDate.Text.Length < 5 ||
(!DateTime.TryParse(txtBirthDate.Text, out output2) ||
!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 5)), out output)))
{
MessageBox.Show("error")
}
However, this might be a good case for the use of a regular expression.
Regex reg = new Regex("^\d{2}/\d{2}/\d{4}$");
if (!reg.IsMatch(txtBirthDate.Text))
MessageBox.Show("error");
Tweaking of the regular expression to match fringe cases (leading zero's, alternate formats, etc) may be necessary.
int result;
if (int.TryParse(txtBirthDate.Text.Substring(test.LastIndexOf("/")), out result))
{
//its good
}
else
{
//its bad
}

C# Input Validation Check for positive numbers

I am learning C# and stuck on a problem where I have to check if the user input a VALID currency amount. i.e. no alphabetical character and no negative numbers.
So far I have everything in the program complete EXCEPT that particular input validation.
to convert the input to numeric values i have:
originalRate = Double.Parse(txtValue.Text);
then below that i am stumped, i have been messing around with:
bool isValid = Double.TryParse(txtValue.Text, );
The common compiler runtime error I get while messing around is Input string was not in a correct format. Which i know it is, that is what I am checking for.
I know this is super basic stuff (this is my first C# class). I have searched around on stack overflow and none of the similar solutions make much sense to me at this point. I have been told to use the TryParse method of the decimal class, however feel as though I am using it wrong and incomplete.
Thank you in advance for your help.
Here's how you use double.TryParse()
double d;
bool isValid = Double.TryParse(txtValue.Text, out d);
the MDSN page has some examples.
To parse currency string, you could use the second overload of double.TryParse()
and try something like
double d;
bool isValid = double.TryParse(txtValue.Text, NumberStyles.Currency, CultureInfo.GetCultureInfo("en-US"), out d);
double result;
if (double.TryParse(txtValue.text, out result))
{
// The user typed a valid number.
// Do something with it (it’s in “result”).
}
else
{
// The user typed something invalid.
MessageBox.Show("Please type in a number.");
}

Categories