I have a textbox accepting user input; I am trying to use this user input it to populate this member of one of my business objects:
public System.Decimal? ExchangeRate
The application is localized - I need to support at the same time cultures that accept these as valid inputs: "1,5" and "1.5"
The code I have now is:
var culture = Thread.CurrentThread.CurrentUICulture;
int exchangeRate;
int.TryParse(txtExchangeRate.Text, NumberStyles.Number, culture,
out exchangeRate);
entity.ExchangeRate = exchangeRate;
When the user culture is set to a culture that expects the "1,5" format (comma as decimal separator) - e.g "ro-RO", I want the value that gets stored in entity.ExchangeRate to be 1.5; however, when running the code above, it gets converted to 15 instead.
Any suggestions on how to convert these various formats so that the data that gets stored in my business entity is "1.5" (point as decimal separator)?
Thanks.
You guys were right - it made sense to use Thread.CurrentThread.CurrentCulture instead of Thread.CurrentThread.CurrentUICulture and decimal.TryParse instead of int.TryParse.
But these changes would still not solve my problem. And after playing around with the code some more, I can now simplify the issue to this:
I am using a telerik RadNumericTextBox control which enforce users to use the correct format based on their culture. So, when Thread.CurrentThread.CurrentCulture is set to "ro-RO", it will only accept the "1,5" format, and when it's set to "en-GB", it will only accept the "1.5" format.
Here's the code I am using now:
decimal exchangeRate;
decimal.TryParse(txtExchangeRate.Text, out exchangeRate);
entity.ExchangeRate = exchangeRate;
Case 1: current culture is "en-GB" - accepted input is "1.5" , exchangeRate is set to 1.5 - everything works fine.
Case 2: current culture is "ro-RO" - accepted input is "1,5" , but after executing the decimal.TryParse... line, exchangeRate is set to 15 - wrong, obviously.
I should also mention that in this case, the value of txtExchangeRate.Text is also shown as "1.5" in my Watch window.
So, it looks like decimal.TryParse will take into consideration the current culture, but I can't find a way to actually make it work properly for me. Any suggestions?
OK, here's the code that seems to work on both cases I described in my above post (1. culture "ro-RO", comma as decimal separator and 2. culture "en-GB", dot as decimal separator):
decimal exchangeRate;
decimal.TryParse(txtExchangeRate.Text, NumberStyles.Any,
CultureInfo.InvariantCulture, out exchangeRate);
entity.ExchangeRate = exchangeRate;
Obviously, int cannot hold 1.5 ! :-) Use float instead.
Use CurrentCulture instead of CurrentUICulture. My culture is fr-BE (therefore accepts 1,5 but my Windows UI is English, which doesn't).
I would make the float.Parse() test with both CurrentCulture AND InvariantCulture: By the time some programs learned to accept "1,5", everybody was used to type "1.5". There's nothing which bothers me more than Excel requiring me to type 1,5 when I say 1.5 ! Also, here in Belgium, the 1st year government launched the web-based tax declaration, the site forced you to use commas instead of periods as decimal points. Everybody was wondering why the figures entered were refused!
So be nice to your users and accept both.
FYI I know this isn't your problem, but its a pointer for other people who might be doing this:
When you set your culture, you can't have your application be able to handle input of different cultures. It must be of the type that you have specified.
Therefore, if you set ro-RO as the culture, it won't understand both 1.5 and 1,5 as the same thing.
You should probably be using CurrentCulture (as opposed to CurrentUICulture) for localization (e.g. date/number formatting).
Related
So as I've stated in my question I recently wrote a small program to calculate the amount of bricks needed to build a room, using the following
float result = ((fLenght * fWitdh) * fHeight) * bricksPerMeter;
When Running a test case on both my main computer and a second computer they come up with a different answer
Ex: PC 1 ((15 * 1)* 1.8)* 40 = 1080
PC 2 with the same values produces 600 as the answer
now the error is fixed by changing the decimal symbol on the second computer
I want to know if there is anyway for my code to check if this is the case and attempt to fix it
without me going to each computer and setting it manually
Normally when you launch a .NET application, it takes its default culture settings from the operating system. That is to say that computer configured for German (Germany) would result in the de-de culture being used in the application, and a computer configured for English (United Kingdom) would result in the en-gb culture being used in the application.
With these cultures come things like date format strings, decimal separators, etc. which is where your issue comes from.
A simple solution is to change your code, which is currently:
float.TryParse(value, out fValue) ? fValue : 1f
to use the Invariant Culture:
float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out fValue) ? fValue : 1f;
This will then use the invariant culture, whose decimal separator is ., to parse the string to a float.
Note that you'll probably want to enforce a culture when you convert the result back to a string. One way to do this is by passing a culture to the .ToString method:
string resultText = result.ToString(CultureInfo.InvariantCulture);
Console.WriteLine(resultText);
Alternative solution (but not recommended): you could override the default culture for your entire application but that might cause problems for you elsewhere, so I don't recommend this.
VS:2005, framework: 2.0
I have a dropdown with French and English languages. As User selects a language, We set CultureInfo as fr-FR or en-US respectively.
When retrieving float values from DB we assign value as
Convert.ToDecimal(table.Rows[0]["diametre"].ToString(), System.Globalization.CultureInfo.InvariantCulture)
In french, 1.3 is displayed as 13 or sometime gives "input string was not in a correct format." error. I tried follwing code:
System.Globalization.NumberFormatInfo numberFormatInfo = new System.Globalization.NumberFormatInfo();
if (System.Globalization.CultureInfo.CurrentCulture.Name == "fr-FR")
numberFormatInfo.NumberDecimalSeparator = ",";
else
numberFormatInfo.NumberDecimalSeparator = ".";
Convert.ToDecimal(table.Rows[0]["densite"].ToString(), numberFormatInfo)
But gives Input String error as above. When "en-US" culture, it works perfectly fine!
What else should I try?
This is giving quite a pain now. Please help me.
TIA
Update:
Thanks all, for responses!
I had help from a colleague while fixing bug. We did following change:
decimal diametre = Convert.ToDecimal(table.Rows[0]["diametre"].ToString().Replace(",", "."), new System.Globalization.CultureInfo("en-US"));
Thanks Tim for useful info like "ToString will use the current culture's decimal separator"
Why is diametre a string at all? If it's a decimal in the database you should use:
decimal diametre = table.Rows[0].Field<decimal>("diametre");
That will be more efficient and also prevents localization issues.
Otherwise you can use decimal.Parse with the appropriate culture-info:
var french = new CultureInfo("fr-FR");
decimal diametre = decimal.Parse(table.Rows[0].Field<string>("diametre"), french);
Update i've only just seen that you're using .NET2, then the Field-extension method is not available. You have to cast it to the correct type, i still wouldn't use ToString to convert everything to string since that could modify it(f.e. if it's a decimal, ToString will use the current culture's decimal separator).
decimal diametre = (decimal)table.Rows[0]["diametre"];
or
decimal diametre = decimal.Parse((string)table.Rows[0]["diametre"], french);
If it's always stored with the dot as decimal separator as in en-US for example, you can use:
decimal diametre = decimal.Parse((string)table.Rows[0]["diametre"], CultureInfo.InvariantCulture);
If you then want to diplay it with the correct format use decimal.ToString:
string output = diametre.ToString(french);
Basiclly, you have 2 cultures in .NET.
You have a culture and you have a UI culture.
The Culture affects how culture-dependent data (dates, currencies, numbers and other information) is being displayed to the user.
The UI culture affects the resource file to be used, so first of all, if you just want to change the language, but keeping the date formates and other information like configured on the computer, you should only change the UI culture and not the culture.
However, the answers given above are valid.
If the field is a number, store it as a number and not a string.
Use Parse with the correct culture.
#Tim I gave you credits for your answer, but I wanted to contribute with this aswell.
I know this question is old, but it might help others who have the same issue and none the above solutions has helped them. For me, adding this line of code to the MainPage constructor of my app (I'm using xamarin.Forms), solved the issue.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Hope it helps, it simply sets the culture of the application to english, regardless of what the devices culture/language has been set to, so if you need the app to behave locally, then this might not be a good solution
You can convert your dbValue first like this:
string MyValue = (String)(table.Rows[0]["diametre"] == DBNull.Value ? string.Empty : table.Rows[0]["diametre"].ToString);
then replace possible comma by dot:
if(!string.isNullOrEmpty(MyValue)){
MyValue.Replace(",",".");
}
and then Convert it to decimal:
Convert.ToDecimal(MyValue, System.Globalization.CultureInfo.InvariantCulture)
As you know, percentage is written left or right side of number in accordance with language.
For example:
In Turkish : %80 | In English : 80%
with localization doing it is really easy.
It is possible to do it with string format and the demonstration can change when culture is changed?
Any idea or help will be appreciated.
You say without localization, do you mean globalization (as Leppie says in comments below)? If so - I'm sorry but this answer basically therefore doesn't answer your question, but comes with a strong advisory not to start reinventing lots of wheels.
From Standard numeric format strings:
decimal percentage = 80.5M;
Console.WriteLine(percentage.ToString("P"));
Formats the number as percentage for the current culture.
If it's a particular culture you're after, rather than the current culture, you have two choices - change the current culture (via the CultureInfo.CurrentCulture and CultureInfo.CurrentUICulture members) or you can simply pass the culture as an argument to ToString():
decimal percentage = 80.5M;
//English (UK)
Console.WriteLine(percentage.ToString("P", CultureInfo.CreateSpecificCulture("en-GB")));
//Turkish (Turkey)
Console.WriteLine(percentage.ToString("P", CultureInfo.CreateSpecificCulture("tr-TR")));
I have float number in string. there is one problem. Number uses "." not "," as decimal point.
This code is not working:
MyNumber = float.Parse("123.5");
I know that I can use string replace function to "repair" this string before Parsing.
MyNumber = float.Parse("123.5".Replace('.',',');
But is there any other way to do it?
Using string replace is very fragile, and will lead to suttle bugs. Specify the IFormatProvider instead. For instance:
MyNumber = float.Parse("123.5", CultureInfo.InvariantCulture);
Or you can specify the NumberFormatInfo using another overload of Parse.
To add to Steven's question, rather than argue differently, the important thing is why the decimal separator is ..
If it's because the source is in a computer-readable format where the period decimal separator is specified as part of the document specification, then I'd go precisely as Steven does in using CultureInfo.InvariantCulture.
If it's human input in a particular locale, then you would want to match that locale by the CultureInfo appropriate for that locale, otherwise if the software is used with a different locale you'd have precisely the opposite problem. Generally you would want to set the thread's CurrentCulture to match this (CurrentCulture for formats, CurrentUICulture for languages). If you've done this, then you don't need to pass a culture at all, as the form float.Parse(string) uses that culture - however, you may wish to use float.Parse(string, CurrentCulture) to be explicit that this is what you are doing (and to shut up some software analysis that complains when you aren't specific in this way).
What gets really tricky, is if you potentially have to accept both period and comma - not least because many cultures that use period as a decimal separator, use comma as a thousands separator, and ultimately it's impossible to guarantee unambiguous parsing. However, assuming the thousands issue doesn't affect you, then the code you gave in your question is the approach, though I'd recommend doing the opposite (replace comma with period) and then parsing with the invariant culture, as that removes any further complications caused by yet more culture changes.
It depends on current culture of currently executed thread culture.
float.Parse("123,5", system.threading.thread.currentthread.currentculture);
float.Parse("123.5", system.threading.thread.currentthread.currentculture);
IF you strictly do not want culturespecific then
float.Parse("123.5", CultureInfo.InvariantCulture);
When I try to convert a string to float:
Console.WriteLine(float.Parse("6.59"));
it throws an exception:
Unhandled Exception: System.FormatException: Input string was not in a correct f
ormat.
at System.Number.ParseSingle(String value, NumberStyles options, NumberFormat
Info numfmt)
When I try it like this:
Console.WriteLine(Convert.ToSingle("6.59"));
It throws the same exception:
Unhandled Exception: System.FormatException: Input string was not in a correct f
ormat.
at System.Number.ParseSingle(String value, NumberStyles options, NumberFormat
Info numfmt)
at System.Convert.ToSingle(String value)
Can you explain why this happens?
The single argument Parse method uses the current culture to parse the string. If your current culture uses some other decimal separator, this will fail.
Try using the invariant culture:
float.Parse("6.59", CultureInfo.InvariantCulture)
The problem here is your culture.
Either set the invariant culture like this:
float.Parse("6.59", CultureInfo.InvariantCulture)
or use the correct decimal separator for your culture
float.Parse("6,59")
I wonder why you are using a literal string. If you are having problems entering literal floats, you can use
Console.WriteLine(6.59f)
If you do it this way culture doesn't matter because the value is decided at compile time.
You are probably using a culture that uses the , as a decimal seperator.
You could try to Parse using the InvariantCulture:
float.Parse("6.59", CultureInfo.InvariantCulture)
Culture - specific things. What's your default culture?
Some cultures use "," instead of ".". You can try this:
float.Parse("6.59", CultureInfo.InvariantCulture);
There could be problem with Locale/Culture. You need to set , instead of . for the decimal separator.
I know everyone here has already given the reason for the problem experienced but perhaps somebody should just expand on why Invariant fixes it.
The CultureInfo class is used either directly or indirectly by classes that format, parse, or manipulate culture-specific data, such as String, DateTime, DateTimeOffset, and the numeric types to deal with the differences in the way different cultures write these types.
In case of the decimal type some cultures use a period(.) whilst others use a comma (,). By default when you are using the Conversion Libraries it will make use of your local culture (that is the country your OS to configured for).
By specifying Invariant you say that you expect thousand separators to be commas(,) and decimal deliminator to be a period(.) as it is in most cultures.
A big problem that sometimes happens is that these cultural conventions change from the OS point of view. For example the South African (ZA) culture info used to behave like the invariant culture. Microsoft changed this with Windows 8 where the decimal suddenly became a comma and the thousand separator a space.This resulted in many legacy systems written in .Net suddently breaking when one migrated them to newer operating systems.
In the end, deal normalize all local culture info to invariant and persist and deal with them in your business logic in this format. Then localize it back on the front end. Same goes for DateTime, as soon as possible convert to UTC, and only back when you render an output.
You could also try Convert class to perform this task.
Convert.ToDecimal("6.59");