I have a C# application that displays some large numbers in a textbox. I want it to use a comma as a thousands separator. I use the following code to do that:
txtNumberDisplay.Text = intNumber.ToString("N0");
The above was working fine until recently it was displaying numbers correctly in the form of:
123,456
Recently the thousands separator has somehow changed to a period "." and the numbers display like this:
123.456
My first thought was the culture had somehow gotten set incorrectly but I checked the default culture for the current thread at the point it displays this number and it is set correctly to "EN-US". Any idea why this would display a period instead of a comma?
Thanks,
use string.Format:
int value = 1234;
string.Format(#"{0:#\,##0}", value);
//Output will be 1,234
EDIT: added an \,
EDIT2: Just found out that this wont work on numbers with 6 digits (two commas would be needed), so here is a workarount, but there must be a better solution...
int value = 1234;
string s = "#";
for (int i = 0; i < value.ToString().Length / 3; i++) s += #"\,###";
string output = string.Format(#"{0:" + s + "}", value);
My first thought was the culture had somehow gotten set incorrectly but I checked the default culture for the current thread at the point it displays this number and it is set correctly to "EN-US". Any idea why this would display a period instead of a comma?
Either the culture is something different than "en-US" or you have customized the number format in the regional settings in the control panel.
When you execute intNumber.ToString("N0") without specifying a culture the culture used is Thread.CurrentThread.CurrentCulture. Perhaps you examined Thread.CurrentThread.CurrentUICulture which is based on the language version of Windows and not used for formatting?
If the current culture really is "en-US" you should go to Control Panel -> Clock, Language, and Region -> Change date, time, or number formats and make sure that the Digit grouping symbol is comma and not dot. The symbol shown in the control panel is the symbol used when you do not specify a CultureInfo when you format the number.
You have several options:
Force the culture when formatting, e.g. intNumber.ToString("N0", CultureInfo.InvariantCulture) to get commas as you desire.
Realize that the user should be able to control the formatting. This is what your code currently does.
This should work perfectly fine:
int.ToString("F", CultureInfo.InvariantCulture);
Windows 8.1 (and probably 8 as well) has incorrect culture regional settings stored.
Therefore, if you rely on ToString("formatstring", desired_culture) it won't work.
Since you write
[...] The above was working fine until recently it was displaying numbers
correctly [...]
I suppose in-between the working and not-working, you upgraded to Windows 8 or 8.1, which explains why it suddenly stopped working.
For example for culture German - Switzerland ("de-CH") it incorrectly sets the NumberDecimalSeparator to "," instead of "." (and the thousand-separator to " " instead of "'") and therefore copy-pasting to Excel won't work anymore (Excel seems to somehow use a non-system format).
You best hardcode your number format, because that's the only way to keep it working across windows-versions.
This is how I do it (you might want to change the separators and the currency-symbol):
private static System.Globalization.NumberFormatInfo SetupNumberFormatInfo()
{
//System.Globalization.NumberFormatInfo nfi = (System.Globalization.NumberFormatInfo)System.Globalization.CultureInfo.InvariantCulture.NumberFormat.Clone();
System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo();
nfi.NumberGroupSeparator = "'";
nfi.NumberDecimalSeparator = ".";
nfi.CurrencyGroupSeparator = "'";
nfi.CurrencyDecimalSeparator = ".";
nfi.CurrencySymbol = "CHF";
return nfi;
} // End Function SetupNumberFormatInfo
private static System.Globalization.NumberFormatInfo m_nfi = SetupNumberFormatInfo();
public static string ToNumberString(this double dblQuantity)
{
return ToNumberString(dblQuantity, "N0");
}
public static string ToNumberString(this double dblQuantity, string Format)
{
// http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo%28VS.71%29.aspx
//double dblQuantity = -123456789.123456789;
//string strFormattedInteger = dblQuantity.ToString("N0", m_nfi);
//strFormattedInteger = string.Format(m_nfi, "{0:N0}", dblQuantity);
return dblQuantity.ToString(Format, m_nfi);
}
Related
I'm beginner in C#, i have a currency field in SQL server, and that field's data type is bigint
For example I have this number:
12598546
I want to show it in this format:
12,598,546
My C# code for it:
query_statusOfRPTOST[i].price_year.ToString("N0", CultureInfo.CreateSpecificCulture("es-US"))
That code in my local PC works fine, but when application runs in server, number is shown in following format:
12598,546
What's happening? And how can I solve that problem?
If you look into NumberFormat of this CultureInfo you will see next:
var culture = CultureInfo.GetCultureInfo("es-US");
Console.WriteLine(string.Join(",",culture.NumberFormat.NumberGroupSizes));
//output: 3,0
What means, according to msdn that to the left of decimal separator first group will contain 3 digits, and other will not be groupped.
If you want to get required value presented in format with groupping, you should do the one of following:
Create specific culture, based on es-US and set NumberGroupSizes as [3].
Use another existing CultureInfo with predefined NumberGroupSizes with [3] - for example, en-US.
Use special formatting, as Ali suggested
Code example for first:
long a = 12598546;
var culture = CultureInfo.CreateSpecificCulture("es-US");
culture.NumberFormat.NumberGroupSizes = new int[] { 3 };
Console.WriteLine(a.ToString("N0", culture));
// output 12,598,546
Code example for second:
long a = 12598546;
Console.WriteLine(a.ToString("N0", CultureInfo.GetCultureInfo("en-US")));
// output 12,598,546
You can use string.Format like this:
string.Format("{0:#,###0}", query_statusOfRPTOST[i].price_year)
Struggling with the basics - I'm trying to code a simple currency converter. The XML provided by external source uses comma as a decimal separator for exchange rate (kurs_sredni):
<pozycja>
<nazwa_waluty>bat (Tajlandia)</nazwa_waluty>
<przelicznik>1</przelicznik>
<kod_waluty>THB</kod_waluty>
<kurs_sredni>0,1099</kurs_sredni>
</pozycja>
I already managed to load the data from XML into a nifty list of objects (kursyAktualne), and now i'm trying to do the math. I'm stuck with conversion.
First of all i'm assigning "kurs_sredni" to a string, trying to replace "," with "." and converting the hell out of it:
string kursS = kursyAktualne[iNa].kurs_sredni;
kursS.Replace(",",".");
kurs = Convert.ToDouble(kursS);
MessageBox.Show(kurs.ToString());
The messagebox show 1099 instead of expected 0.1099 and kursS still has comma, not dot.
Tried toying with some cultureInfo stuff i googled, but that was too random. I need to understand how to control this.
Just use decimal.Parse but specify a CultureInfo. There's nothing "random" about it - pick an appropriate CultureInfo, and then use that. For example:
using System;
using System.Globalization;
class Test
{
static void Main()
{
var french = CultureInfo.GetCultureInfo("fr-FR");
decimal value = decimal.Parse("0,1099", french);
Console.WriteLine(value.ToString(CultureInfo.InvariantCulture)); // 0.1099
}
}
This is just using French as one example of a culture which uses , as a decimal separator. It would probably make sense to use the culture of the origin of the data.
Note that decimal is a better pick for currency values than double - you're trying to represent an "artificial" construct which is naturally specified in base10, rather than a "natural" continuous value such as a weight.
(I would also be wary of a data provider who provides data in a non-standard format. If they're getting that wrong, who knows what else they'll get wrong. It's not like XML doesn't have a well-specified format for numbers...)
It is because Replace method returns new string with replaced characters. It does not modify your original string.
So you need to reassign it:
kursS = kursS.Replace(",",".");
Replace returns a string. So you need an assignment.
kursS = kursS.Replace(",", ".");
There is "neater" way of doing this by using CulturInfo. Look this up on the MSDN website.
You replace result isn't used, but the original value that doesn't contain the replace.
You should do:
kursS = kursS.Replace(",", ".")
In addition this method isn't really safe if there are thousands-separators.
So if you are not using culture settings you should do:
kursS = kursS.Replace(".", "").Replace(",", ".")
I'm receiving from the Oracle database the following value (as varchar): 7876,72
In Visual Studio it's read as a String
CASE 1 (expected case):
When I'm debuggin I see: 7876.72 and when I try to Convert.ToDecimal("7876.72") I'm getting 7876.72D
CASE 2 (wrong case):
When I'm debuggin I see: 7876,72 and when I try to Convert.ToDecimal("7876,72") I'm getting 787672D
I'm checking the CultureInfo.CurrentCulture and RegionInfo.CurrentRegion and it's the same on both machines...
The main reason of this is because:
Convert.ToDecimal("7876,72").ToString("0,0.00", CultureInfo.InvariantCulture) gives me: 787,672.00 and it should be 7,876.72.
Global.asax - Application_Start
Dim newCulture As CultureInfo = DirectCast(System.Threading.Thread.CurrentThread.CurrentCulture.Clone(), CultureInfo)
newCulture.DateTimeFormat.ShortDatePattern = "yyyy/MM/dd"
newCulture.DateTimeFormat.DateSeparator = "/"
newCulture.NumberFormat.CurrencyDecimalSeparator = "."
newCulture.NumberFormat.NumberDecimalSeparator = "."
Thread.CurrentThread.CurrentCulture = newCulture
Any ideas on how to keep the same value on both machines?
Thanks in advance!
You are getting the output mentioned above as not expected because the following value 7876,72 is equal to 787672. The comma is just used to separate the numbers but it is not used to indicate a decimal point when converting. However, the decimal point is represented by the following character .
I'd recommend you to replace the character , with . since you would like to get a decimal point instead of ,.
Example
string NumberFromDatabase = "7876,72"; //Initialize a new string of name NumberFromDatabase as 7876,72
string NumberAfterReplacement = NumberFromDatabase.Replace(',', '.'); //Initialize a new string of name NumberAfterReplacement as 7876.72
decimal Output = Convert.ToDecimal(NumberAfterReplacement); //Initialize a new decimal of name Output as 7876.72
Thanks,
I hope you find this helpful :)
For numbers: Use TryParse(...) with an appropriate NumberStyle
For Dates: It's a good habit to ParseExact
The problem was with the Oracle client, I had some inconsistency between my developer machine and the server machine, that was the main reason why the dates and the decimal values where showing differente on both machines.
The solution was forcing both machines to behave the same way, and to do that we changed the key of the Oracle client and added the needed parameters:
Hope it helps someone else.
How can I change values in string from 0,00 to 0.00? - only numeric values, not all chars "," to "."
FROM
string myInputString = "<?xml version=\"1.0\"?>\n<List xmlns:Table=\"urn:www.navision.com/Formats/Table\"><Row><HostelMST>12,0000</HostelMST><PublicMST>0,0000</PublicMST><TaxiMST>0,0000</TaxiMST><ParkMST>0,0000</ParkMST><RoadMST>0,0000</RoadMST><FoodMST>0,0000</FoodMST><ErrorCode>0</ErrorCode><ErrorDescription></ErrorDescription></Row></List>\n";
TO
string myInputString = "<?xml version=\"1.0\"?>\n<List xmlns:Table=\"urn:www.navision.com/Formats/Table\"><Row><HostelMST>12.0000</HostelMST><PublicMST>0.0000</PublicMST><TaxiMST>0.0000</TaxiMST><ParkMST>0.0000</ParkMST><RoadMST>0.0000</RoadMST><FoodMST>0.0000</FoodMST><ErrorCode>0</ErrorCode><ErrorDescription></ErrorDescription></Row></List>\n";
Thanks for answers, but I mean to change only numeric values, not all chars "," to "."
I don't want change string from
string = "<Attrib>txt txt, txt</Attrib><Attrib1>12,1223</Attrib1>";
to
string = "<Attrib>txt txt. txt</Attrib><Attrib1>12.1223</Attrib1>";
but this one is ok
string = "<Attrib>txt txt, txt</Attrib><Attrib1>12.1223</Attrib1>";
Try this :
Regex.Replace("attrib1='12,34' attrib2='43,22'", "(\\d),(\\d)", "$1.$2")
output : attrib1='12.34' attrib2='43.22'
The best method depends on the context. Are you parsing the XML? Are you writing the XML. Either way it's all to do with culture.
If you are writing it then I am assuming your culture is set to something which uses commas as decimal seperators and you're not aware of that fact. Firstly go change your culture in Windows settings to something which better fits your culture and the way you do things. Secondly, if you were writing the numbers out for human display then I would leave it as culturally sensative so it will fit whoever is reading it. If it is to be parsed by another machine then you can use the Invariant Culture like so:
12.1223.ToString(CultureInfo.InvariantCulture);
If you are reading (which I assume is what you are doing) then you can use the culture info again. If it was from a human source (e.g. they typed it in a box) then again use their default culture info (default in float.Parse). If it is from a computer then use InvariantCulture again:
float f = float.Parse("12.1223", CultureInfo.InvariantCulture);
Of course, this assumes that the text was written with an invariant culutre. But as you're asking the question it's not (unless you have control over it being written, in which case use InvariantCulture to write it was suggested above). You can then use a specific culture which does understand commas to parse it:
NumberFormatInfo commaNumberFormatInfo = new NumberFormatInfo();
commaNumberFormatInfo.NumberDecimalSeperator = ",";
float f = float.Parse("12,1223", commaNumberFormatInfo);
I strongly recommend joel.neely's regex approach or the one below:
Use XmlReader to read all nodes
Use double.TryParse with the formatter = a NumberFormatInfo that uses a comma as decimal separator, to identify numbers
Use XmlWriter to write a new XML
Use CultureInfo.InvariantCulture to write the numbers on that XML
The answer from ScarletGarden is a start, but you'll need to know the complete context and grammar of "numeric values" in your data.
The problem with the short answer is that cases such as this get modified:
<elem1>quantity<elem2>12,6 of which were broken</elem2></elem1>
Yes, there's probably a typo (missing space after the comma) but human-entered data often has such errors.
If you include more context, you're likely to reduce the false positives. A pattern like
([\s>]-?$?\d+),(\d+[\s<])
(which you can escape to taste for your programming language of choice) would only match when the "digits-comma-digits" portion (with optional sign and currency symbol) was bounded by space or an end of an element. If all of your numeric values are isolated within XML elements, then you'll have an easier time.
string newStr = myInputString.Replace("0,00", "0.00");
While you could theoretically do this using a Regex, the pattern would be complex and hard to to test. ICR is on the right track, you need to do this based on culture.
Do you know that your numbers are always going to be using a comma as a decimal separator instead of a period? It looks like you can, given that Navision is a Danish company.
If so, you'll need to traverse the XML document in the string, and rewrite the numeric values. It appears you can determine this on node name, so this won't be an issue.
When you convert the number, use something similar to this:
here's what you want to do:
internal double ConvertNavisionNumber(string rawValue)
{
double result = 0;
if (double.TryParse(rawValue, NumberStyles.Number, new CultureInfo("da-DK").NumberFormat, out result))
return result;
else
return 0;
}
This tells the TryParse() method that you're converting a number from Danish (da-DK). Once you call the function, you can use ToString() to write the number out in your local format (which I'm assuming is US or Canadian) to get a period for your decimal separator. This will also take into account numbers with different thousands digit separator (1,234.56 in Canada is written as 1 234,56 in Denmark).
ConvertNavisionNumber("4,43").ToString()
will result in "4.43".
ConvertNavisionNumber("1 234").ToString()
will result in "1,234".
if the , is not used anywhere else but number with in the string you can use the following:
string newStr = myInputString.Replace(",", ".");
What is the best way to parse a float in CSharp?
I know about TryParse, but what I'm particularly wondering about is dots, commas etc.
I'm having problems with my website. On my dev server, the ',' is for decimals, the '.' for separator. On the prod server though, it is the other way round.
How can I best capture this?
I agree with leppie's reply; to put that in terms of code:
string s = "123,456.789";
float f = float.Parse(s, CultureInfo.InvariantCulture);
Depends where the input is coming from.
If your input comes from the user, you should use the CultureInfo the user/page is using (Thread.CurrentThread.CurrentUICulture).
You can get and indication of the culture of the user, by looking at the HttpRequest.UserLanguages property. (Not correct 100%, but I've found it a very good first guess) With that information, you can set the Thread.CurrentThread.CurrentUICulture at the start of the page.
If your input comes from an internal source, you can use the InvariantCulture to parse the string.
The Parse method is somewhat easier to use, if your input is from a controlled source. That is, you have already validated the string. Parse throws a (slow) exception if its fails.
If the input is uncontrolled, (from the user, or other Internet source) the TryParse looks better to me.
If you want persist values ( numbers, date, time, etc... ) for internal purpose. Everytime use "InvariantCulture" for formating & parsing values. "InvariantCulture" is same on every computer, every OS with any user's culture/language/etc...
string strFloat = (15.789f).ToString(System.Globalization.CultureInfo.InvariantInfo);
float numFloat = float.Parse(System.Globalization.CultureInfo.InvariantInfo, strFloat);
string strNow = DateTime.Now.ToString(System.Globalization.CultureInfo.InvariantInfo);
DateTime now = DateTime.Parse(System.Globalization.CultureInfo.InvariantInfo, strNow);
You could always use the overload of Parse which includes the culture to use?
For instance:
double number = Double.Parse("42,22", new CultureInfo("nl-NL").NumberFormat); // dutch number formatting
If you have control over all your data, you should use "CultureInfo.InvariantCulture" in all of your code.
Use a neutral culture (or one you know) when parsing with Try/Parse.
Pass in a CultureInfo or NumberFormatInfo that represents the culture you want to parse the float as; this controls what characters are used for decimals, group separators, etc.
For example to ensure that the '.' character was treated as the decimal indicator you could pass in CultureInfo.InvariantCulture (this one is typically very useful in server applications where you tend to want things to be the same irrespective of the environment's culture).
Try to avoid float.Parse, use TryParse instead as it performs a lot better but does the same job.
this also applies to double, DateTime, etc...
(some types also offer TryParseExact which also performs even better!)
The source is an input from a website. I can't rely on it being valid. So I went with TryParse as mentioned before.
But I can't figure out how to give the currentCulture to it.
Also, this would give me the culture of the server it's currently running on, but since it's the world wide web, the user can be from anywhere...
you can know current Cuklture of your server with a simple statement:
System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture;
Note that there id a CurrentUICulture property, but UICulture is used from ResourceMeanager form multilanguages applications. for number formatting, you must considere CurrentCulture.
I hope this will help you
One approach is to force localization to use dot instead of comma separator - this way your code will work identically on all windows machines independently from selected language and settings.
This approach is applicable to small gained applications, like test applications, console applications and so on. For application, which was localization in use this is not so useful, but depends on requirements of application.
var CurrentCultureInfo = new CultureInfo("en", false);
CurrentCultureInfo.NumberFormat.NumberDecimalSeparator = ".";
CurrentCultureInfo.NumberFormat.CurrencyDecimalSeparator = ".";
Thread.CurrentThread.CurrentUICulture = CurrentCultureInfo;
Thread.CurrentThread.CurrentCulture = CurrentCultureInfo;
CultureInfo.DefaultThreadCurrentCulture = CurrentCultureInfo;
This code forces to use dot ('.') instead of comma, needs to be placed at application startup.
Since you don't know the web user's culture, you can do some guesswork. TryParse with a culture that uses , for separators and . for decimal, AND TryParse with a culture that uses . for separators and , for decimal. If they both succeed but yield different answers then you'll have to ask the user which they intended. Otherwise you can proceed normally, given your two equal results or one usable result or no usable result.