I'm writing code with german culture settings
Nevertheless I would like to force the user to use the point as a decimal separator.
My piece of test code outputs the wrong value.
How do I detect the "wrong" comma ?(throw an exception)
string nok_str = "14,9";
string ok_str = "14.9";
double nok_Var1 = double.Parse(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
double nok_Var2 =Convert.ToDouble(nok_str, CultureInfo.InvariantCulture.NumberFormat); // outputs 149.0
First off, and please forgive me, Iād like to question your design decision:
How is this enhancing the user experience? The application should rather try to accept all unambiguous user input, not reject theoretically sound input.
That said, a number such as ā19,2ā will be interpreted, with an invariant culture, as having a thousands separator (which is simply discarded). This is why your code silently produces bad values. If you want to explicitly forbid this input, the easiest way to achieve this is an explicit test:
if (nok_str.Contains(","))
throw new FormatException(ā¦);
As an alternative, you can try modifying the NumberFormatInfo.NumberGroupSeparator property of a custom NumberFormatInfo object that you pass to the Parse method.
Basically the default is to include AllowThousands in the number style. If you specify the number style you want, you can prohibit this:
using System;
using System.Globalization;
class Test
{
static void Main(string[] args)
{
string text = "19,2";
double value;
bool valid = double.TryParse(text, NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
Console.WriteLine(valid); // Prints false
}
}
Note that NumberStyles.Float is a composite style for AllowLeadingWhite, AllowTrailingWhite, AllowLeadingSign, AllowDecimalPoint, and AllowExponent - but not AllowThousands.
I am not sure what is the source of your input.
If it comes from user it also depends... If it is GUI application, you may think of restricting the input to certain possible keys, excluding comma. If it is a console app, you can try regular expressions to pre-validate input strings.
If it comes from various sources (i.e. web service) maybe simply brute-force string replace will do the trick?
Last, but not least: there are reasons for parsing to be culture-sensitive and if I were you, I would encourage users to enter valid regional number format instead forcing them to provide incorrect one.
Related
I am new to C # and I am currently having problems with the following. In C #, I have a Pi floating point number and I want to convert it to a string using the ToString() method. But the conversion gives a string result with a comma "3,1415". On another machine, the same gives the string result with the dot "3.1415". What is the reason for this and what should I do to get a dotted string result?
EDIT: The problem is, I can't change the code, but I can install and uninstall .Net frameworks, change my OS settings, etc.
Edit: if you can't change the code. Change the language/localization of the system to one which uses dot as decimal separator. In Control Panel or Settings.
You should look at internationalization and localization in the System.Globalization namespace.
The advice here is to use one CultureInfo specific for parsing numbers or writing numbers to string.
var flt = 232.23f;
var str = flt.ToString(CultureInfo.InvariantCulture); //for example, you can use CultureInfo.CurrentCulture
This allows you to keep the ThreadCulture without change it.
But take a look at this link https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo?view=net-5.0 .Take your time, is dense.
I would just set the current culture at the entry point of your program.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
However I would also check the regional settings and so forth on the machine where the comma separator appears.
It is related with current culture info. You can specify the culture info in ToString method as a parameter like;
var convertedFloat = floatVariable.ToString(new CultureInfo("en-GB"));
Thanks to GSerg, for comment about Windows regional settings. That solves my problem. In the Windows Control panel enter Region and Language. In the Formats tab click Additional Settings and in the Decimal symbol field specify what decimal separator must be used when converting a floating point number to a string.
I just need to know if the value is numeric. I don't need to do anything with the value. Is this the best way? Feel dirty creating a variable that I won't ever use beyond this:
int val;
if(int.TryParse(txtFoo.Text, out val))
{
....
}
Yes, using the relevant TryParse method and ignoring the out parameter is the best way of doing this.
You may want to wrap this up into your own set of helper methods (which could specify the appropriate culture etc, if the default isn't right for you) and just return a bool without the out parameter to make them easier to call.
Of course, you need to work out what kind of parsing is most appropriate - even for integers, you need to consider whether the range of Int32 is enough for your use case. In my experience, most numeric input has its own "natural" range of valid values, which is unlikely to be exactly the range of any predefined type. You may therefore want to expand your helper methods to include the range of valid values to accept.
"is numeric" is an ambiguous term.
Culture-aware?
Allow thousands and/or decimal separators?
Allow scientific notation?
Allow a sign (before? after?...)
What range of values do you allow? Signed 32-bit integer (Int32.TryParse), Unsigned 32-bit integer (UInt32.TryParse), decimal, double, ...
Hence there is no "best" way, and the Framework provides a multitude of different ways to parse numbers.
You can use Regular expressions
Regex _isNumber = new Regex(#"^\d+$");
_isNumber.IsMatch(txtFoo.Text);
This will only match Ints, but you can write one that also matches decimals.
It's not as flexible as int.TryParse, but you could check to see if each character is a number:
bool isInt = txtFoo.Text.All(c => char.IsNumber(c));
In general, though, I would recommend sticking with int.TryParse. You can even call the unused parameter "ignored" to be explicit about your intent, e.g.:
int ignored;
bool isInt = int.TryParse(txtFoo.Text, out ignored);
That is the recommended way of doing it in C#. However, you could also add Microsoft.VisualBasic.dll as a reference to your project and then use Microsoft.VisualBasic.Information.IsNumeric()
You can try using Regex parsing to determine that there are no non-numeric characters in a string, or you can use Int.TryParse(), Double.TryParse(), Float.TryParse() depending on the input.
bool test (string teststring)
{ for (i=0;i==teststring.length;i++){
if instr("0123456789.,-+Ee",teststring.substring(i,1) <0){return false;}
// some additional tests below here if you like
return true;
}
however E1001E12e.12e would be noted as a number a little bit more magic is needed to do a clean check, but then you might be able to determine if its a int or a float too..
That's the best way of doing it in my knowledge - that's what our company standards adhere to anyway due to the error handling being done within the parsing.
This details the advantages: https://web.archive.org/web/20150510214425/http://www.dotnetperls.com:80/int-tryparse
I'm currently doing an app, that needs to be able to work with the US number layout (123,456.78) as well as with the German layout (123.456,78).
Now my approach is to use NumberFormatInfo.InvariantInfo about like this:
temp = temp.ToString(NumberFormatInfo.InvariantInfo);
this works great when for example reading a number from a textbox. When System is set to English format it will take the . as separator, when it's set to German it will use the ,.
So far so good....but here's the problem: I have a device that returns info in the American format, and that won't change (transmitted via RS232). So I receive something like 10.543355E-00.
Now when on German setting the . will be discarded since it's just the group separator
and the number I will end up with is 10543355....which is a lot more :)
I tried with the same technique thinking this would make the whole thing kind of 'cultureless' to be able to process it independently from the system language but it didn't work :)
I hope you can maybe help me here...I'd love to use a way without having to implement the whole culture stuff etc since all I need here is really numbers that get calculated the right way.
You should use CultureInfo.InvariantCulture when parsing strings from the device. This will cause it to use the invariant culture, which has the US rules for decimal separation.
Edit in response to comments:
The issue is not when you call .ToString(), but rather when you read the string from the device, and convert it to a number:
string inputFromRS232Device = GetDeviceInput();
double value;
// You need this when converting to the double - not when calling ToString()
bool success = double.TryParse(
inputFromRS232Device,
NumberStyles.Float,
CultureInfo.InvariantCulture,
out value);
I noticed my doubles contain different precision characters depending on regional settings.
For example:
3,47 or 3.45
How can I enforce the double should always contain a . precision character?
Problem is once I serialize the class containing this double to XML, it gets shipped to other systems expecting a standard result.
So after reading your responses (and thanks), do you guys recommend changing the property to a string, (making the replacements in a string), so that it serializes with the string value (not the double)?
You need to put the double to string using the Invariant Culture.
double d = 3.47;
Console.WriteLine(d.ToString(CultureInfo.InvariantCulture));
You will need to format with the InvariantCulture.
Note that the "." and "," formatting characters are interpreted according to the culture settings.
This little tutorial will be the answer you need, I expect:
http://www.java2s.com/Tutorial/CSharp/0440__I18N-Internationalization/Parsingnumberswithcultureinvariantparsing.htm
double.Parse(numberString, CultureInfo.InvariantCulture);
The double itself doesn't include a "." or a ",", only the print out of the representation does. You can read up on custom formats here.
[Update according to OP]
I don't know exactly what your design looks like, but it would probably be smart to create a string property on your DTO, which would output the formatted string of your double, and then mark your double property as not serializable.
When you need to do this for all numbers in your current applicaton, you can use the following to set it application-wide (new threads will inherit this setting):
// use this statement to force the default:
Application.CurrentCulture = CultureInfo.InvariantCulture;
string s = myNumber.ToString();
// for one number you have to remember to use:
string s = myNumber.ToString(CultureInfo.InvariantCulture);
Note: by default, your application, whether ASP.NET or WinForms, will use the culture settings of the system it is running on (in ASP.NET, you can set the culture globally in the web.config).
When doing a string comparison in C#, what is the difference between doing a
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.CurrentCultureIgnoreCase);
and
string test = "testvalue";
test.Equals("TESTVALUE", StringComparison.InvariantCultureIgnoreCase);
... and is it important to include that extra parameter, anyway?
The other posts have given good advice, but I thought it might be nice to show an example of where it definitely makes a difference:
using System;
using System.Globalization;
using System.Threading;
class Test
{
static void Main()
{
CultureInfo turkish = CultureInfo.CreateSpecificCulture("tr");
Thread.CurrentThread.CurrentCulture = turkish;
// In Turkey, "i" does odd things
string lower = "i";
string upper = "I";
// Prints False
Console.WriteLine(lower.Equals(upper,
StringComparison.CurrentCultureIgnoreCase));
// Prints True
Console.WriteLine(lower.Equals(upper,
StringComparison.InvariantCultureIgnoreCase));
}
}
(There are no doubt many other cases - this was just the first one I thought of.)
Microsoft gives some decent guidance for when to use the InvariantCulture property:
MSDN: CultureInfo.InvariantCulture Property
... an application should use the
invariant culture only for processes
that require culture-independent
results, such as formatting and
parsing data that is persisted to a
file. In other cases, it produces
results that might be linguistically
incorrect or culturally inappropriate.
Security Considerations
If a security decision will be made
based on the result of a string
comparison or case change, your
application should use an ordinal
comparison that ignores case instead
of using InvariantCulture. [...]
String Operations
If your application needs to perform a
culture-sensitive string operation
that is not affected by the value of
CurrentCulture, it should use a method
that accepts a CultureInfo parameter.
[...]
Persisting Data
The InvariantCulture property is
useful for storing data that will not
be displayed directly to users.
Storing data in a culture-independent
format guarantees a known format that
does not change. When users from
different cultures access the data, it
can be formatted appropriately based
on specific user. [...]