Convert Standard Numeric Format with culture to Custom Numeric Format - c#

In C#, is there a way to get the equivalent custom numeric format for a standard numeric format with a specified user's culture?
Examples (not sure my conversion are right) :
C3 in fr-FR = 0.000 '€'
D2 = 0.00
P0 = %#0
See those links :
http://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/0c899ak8(v=vs.110).aspx

Given a CultureInfo you can examine the NumberFormat property of type NumberFormatInfo to get all the information that .NET uses when formatting different number types. E.g.:
var frenchCultureInfo = CultureInfo.GetCultureInfo("fr-FR");
Console.WriteLine(frenchCultureInfo.NumberFormat.CurrencySymbol);
This will print €. To reconstruct the complete format you will have to inspect multiple properties on the NumberFormat property. This can become quite tedious. As an experiment I have tried to write the code necessary to format a Decimal using the C format specifier:
var value = -123456.789M;
var cultureInfo = CultureInfo.GetCultureInfo("as-IN");
var numberFormat = cultureInfo.NumberFormat;
// Create group format like "#,##,###".
var groups = numberFormat
.CurrencyGroupSizes
.Reverse()
.Select(groupSize => new String('#', groupSize));
var format1 = "#," + String.Join(",", groups);
// Create number format like "#,##,##0.00".
var format2 = format1.Substring(0, format1.Length - 1)
+ "0." + new String('0', numberFormat.CurrencyDecimalDigits);
// Format the number without a sign.
// Note that it is necessary to use the correct CultureInfo here.
var formattedNumber = Math.Abs(value).ToString(format2, cultureInfo);
// Combine sign, currency symbol and number.
var currencyPositivePatterns = new Dictionary<Int32, String> {
{ 0, "{0}{1}" },
{ 1, "{1}{0}" },
{ 2, "{0} {1}" },
{ 3, "{1} {0}" }
};
var currencyNegativePatterns = new Dictionary<Int32, String> {
{ 0, "({0}{1})" },
{ 1, "-{0}{1}" },
{ 2, "{0}-{1}" },
{ 3, "{0}{1}-" },
{ 4, "({1}{0})" },
{ 5, "-{1}{0}" },
{ 6, "{1}-{0}" },
{ 7, "{1}{0}-" },
{ 8, "-{1} {0}" },
{ 9, "-{0} {1}" },
{ 10, "{1} {0}-)" },
{ 11, "{0} {1}-" },
{ 12, "{0} -{1}" },
{ 13, "{1}- {0}" },
{ 14, "({0} {1})" },
{ 15, "({1} {0})" }
};
var currencyPattern = value >= Decimal.Zero
? currencyPositivePatterns[numberFormat.CurrencyPositivePattern]
: currencyNegativePatterns[numberFormat.CurrencyNegativePattern];
var formattedValue = String.Format(
currencyPattern,
numberFormat.CurrencySymbol,
formattedNumber
);
The value of formattedValue is ₹ -1,23,456.79 which is the same as you get when evaluating value.ToString("C", cultureInfo). Obviously, the later is much simpler.
Note that some currency symbols can contain . or ' which have special meaning in a custom numeric format. As a result of this you cannot always create a custom format string to replace C. E.g. for the da-DK culture the C format for a positive number is equivalent to kr. #,##0.00 except the dot after kr will make it impossible to use that format. Instead you have to use an approach where the currency symbol is added after the number has been formatted, or alternatively you can escape the problematic character.

Afaik unlike decimal separator or thousands separator, there is no format string for the cultures currency. Maybe this is due to the fact that a number is always the same in every culture, but an amount of money is different according to the currency. Thus, you would have to know the currency in advance.
However, if you do know the currency in advance, you can just use the currency symbol as a normal literal character.
Thus, you could use "0.000 €" (don't French people use the , as their decimal separator?).

If you know the culture in advance, you can get the format string that's used for currency and use it yourself.
In your example, you would retrieve the CultureInfo object for the fr-FR culture using this:
CultureInfo French = new CultureInfo("fr-FR");
You could just pass this CultureInfo object to the ToString() method, or you could get the NumberFormatInfo object from the CultureInfo:
NumberFormatInfo numberFormat = French.NumberFormat;
The NumberFormatInfo object has the following properties: CurrencySymbol, CurrencyDecimalDigits, CurrencyDecimalSeparator, 'CurrencyGroupSeparator,CurrencyGroupSizes`. Using them, you can construct your own format string:
string fmt = numberFormat.CurrencySymbol + "#" + numberFormat.CurrencyGroupSeparator;
for ( int i = 0; i < numberFormat.CurrencyGroupSizes - 1; i++ )
fmt += "#";
fmt += "0" + numberFormat.CurrencyDecimalSeparator;
for ( int d = 0; i < numberFormat.CurrencyDecimalDigits; i++ )
fmt += "0";
There are two other properties of the NumberFormatInfo class called CurrencyNegativePattern and CurrencyPositivePattern, which tell you where the decimal symbol & sign go, or whether to put negative values in parentheses. I'll leave it to you to decide if you need these & write the code necessary to build the format string.

Related

How to truncate a number to a fixed size without rounding when displaying it with Console.WriteLine()

I'm trying to format a number in a very specific way when displaying it, I've tried messing around with String.Format but couldn't find a way to make this work exactly as I needed.
For example, let's way that I have a variable of type Double with the value "123459.7889" and that I want to format it as "459.788" (000.000) but only during Console.WriteLine() and without rounding or changing the internal value of the number. The full value of the number would not show up and it should technically "overflow" the string because it should only print the first 3 numbers to the left of the decimal point.
Here are some example inputs and example outputs:
12345.678 formatted to "000.000" would become 345.678
1000000.678 formatted to "000.000" would become 000.678
1.777888 formatted to "000.000" would become 001.777
99999.9 formatted to "000.000" would become 999.900
Basically, no matter the internal size of the number, the formatted output should always be the 3 numbers to the left of the decimal point and the 3 numbers to the right of the decimal point with zeros to replace the missing spaces if there's any.
I've tried looking at the examples on the C# documentation and found this:
double value = 123459.7889;
string result = String.Format("{0,8:00000000} {0,8:00000000.000}", value);
Console.WriteLine(result);
But it doesn't work exactly like I needed it to. When running it the numbers are rounded so it becomes 00123459.789 instead of 00123459.788 for example and if the number grows it no longer stays at that fixed size.
try this:
double value = 123459.7889;
Console.WriteLine((Math.Truncate(Math.Abs(value) % 1000) + double.Parse((Math.Round(Math.Abs(value), 3, MidpointRounding.ToZero) -
Math.Truncate(Math.Abs(value))).ToString(".000"))).ToString("000.000"));
value= 123459.7889; result=345.678
value= 1000000.678; result=000.678
value= 1.777888; result=001.777
value= 99999.9; result=999.900
value= 152; result=152 .000
value= -100.02; result=100.020
value= 10.0005; result=010.000
We can use PadLeft and PadRight to fill up zeros when value characters are not enough.
Implementation:
static void Main(string[] args)
{
double value = 1.12;
string valueStr = value.ToString().Replace("-", ""); // convert to string
// check if value does not contain decimal and add
if (!valueStr.ToString().Contains("."))
valueStr += valueStr + ".0"; // add decimal part
var arr = valueStr.ToString().Split(".");
string intValue = arr[0]; //1
intValue = intValue.PadLeft(3,'0'); //001
intValue = intValue.Substring(intValue.Length - 3, 3); //001
string decimalValue = arr[1]; //12
decimalValue = decimalValue.PadRight(3,'0'); //120
decimalValue = decimalValue.Substring(0, 3); //120
string result = $"{intValue}.{decimalValue}";
Console.WriteLine(result);
}
Result:
12345.678 > 345.678
1000000.678 > 000.678
1.777888 > 001.777
99999.9 > 999.900
152 > 152.000
-100.02 > 100.020
-10.0005 > 010.000
You shouldn't need to convert to string, nor should you rely on splitting on a character that may or may not be the decimal character in the language you are running. You can do all the calculations on the numbers and then format the output without allocating any strings except for the result.
public static string FormatNumber(double number)
{
int first = ((int)number)%1000;
int second = (int)Math.Truncate((number-(int)number)*1000)%1000;
return $"{first:000}.{second:000}";
}
static string Format(double number)
{
return string.Format("{0:N3}", number % 1000).PadLeft(7,'0');
}
How it works:
Remove the upper digits using modulus (%)
Format to three decimal places using a format string
Pad left using PadLeft()
Full example:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var tests = new Dictionary<double,string>
{
{ 12345.678D, "345.678" },
{ 1000000.678, "000.678" },
{ 1.777888, "001.778" },
{ 99999.9, "999.900"}
};
foreach (var pair in tests)
{
Console.WriteLine("Expected: {0} Actual: {1}", pair.Value, Format(pair.Key));
}
}
static string Format(double number)
{
return string.Format("{0:N3}", number % 1000).PadLeft(7,'0');
}
}
Output:
Expected: 345.678 Actual: 345.678
Expected: 000.678 Actual: 000.678
Expected: 001.778 Actual: 001.778
Expected: 999.900 Actual: 999.900
Fiddle
try this
int i = value.ToString().IndexOf(".");
string result = string.Concat(value.ToString().Substring(i - 3, 3),".", value.ToString().Substring(i + 1, 3));
Console.WriteLine(result);

How to get year in Hong kong language (culture info : zh-hk)

I want to match the string in Hong kong language
I have month and year as below in hongkong language
二零一六年六月份 ===>June 2016
二零一五年六月份 ===>June 2015
I have use culture info (zh-HK) to get month like
But how to get year? Please help
Basically, you need to create a dictionary that uses the Chinese characters as the key and the corresponding numbers as the value:
var dict = new Dictionary<String, String>() {
{"零", "0"},
{"一", "1"},
{"二", "2"},
{"三", "3"},
{"四", "4"},
{"五", "5"},
{"六", "6"},
{"七", "7"},
{"八", "8"},
{"九", "9"},
{"十", "1"} // this is needed for the months to work. If you know Chinese you would know what I mean
};
Then, you split the input string with the separator "年":
string[] twoParts = inputString.Split('年');
You loop through each character of the first part. Using the dictionary you created, you can easily get 2016 from "二零一六".
For the second part, check whether "份" is present at the end. If it is, substring it off. (sometimes months can be written without "份"). After that, do one more substring to get rid of the "月".
Now you use the dictionary above again to turn something like "十二" to "12"
Now you have the year and the month, just create a new instance of DateTime!
Here's the full code:
string inputString = ...;
var dict = new Dictionary<String, String>() {
{"零", "0"},
{"一", "1"},
{"二", "2"},
{"三", "3"},
{"四", "4"},
{"五", "5"},
{"六", "6"},
{"七", "7"},
{"八", "8"},
{"九", "9"},
{"十", "1"} // this is needed for the months to work. If you know Chinese you would know what I mean
};
string[] twoParts = inputString.Split ('年');
StringBuilder yearBuilder = new StringBuilder ();
foreach (var character in twoParts[0]) {
yearBuilder.Append (dict [character.ToString ()]);
}
string month = twoParts [1];
if (month [month.Length - 1] == '份') {
month = month.Substring (0, month.Length - 1);
}
month = month.Substring (0, month.Length - 1);
StringBuilder monthBuilder = new StringBuilder ();
foreach (var character in month) {
monthBuilder.Append (dict [character.ToString ()]);
}
var date = new DateTime (Convert.ToInt32 (yearBuilder.ToString()), Convert.ToInt32 (monthBuilder.ToString()), 1);
Console.WriteLine (date);
EDIT:
I just realized that this doesn't work if the month is October, in which case it will parse to January. To fix this, you need to use a separate dictionary for the months. Since the SE editor doesn't allow me to enter too many Chinese characters, I will try to tell you want to put in this dictionary in the comments.
When you parse the months, please use the new dictionary. So now the month parsing code will look like this:
month = month.Substring (0, month.Length - 1);
string monthNumberString = newDict[month];
No need for the for each loop.

How to display at most N decimals and no decimals if not needed in .Net?

say I have these 3 floats:
10,
10.12
10.234
And I want to print at most 1 decimal, but no decimals if not needed, like this:
10
10.1
10.2
You are looking for "#.#" format in ToString. Use it like:
decimal d = 10M;
string str = d.ToString("#.#", CultureInfo.InvariantCulture);
Consider the following example:
List<float> list = new List<float> { 10f, 10.12f, 10.234f };
foreach (var item in list)
{
Console.WriteLine("{0} => {1}", item, item.ToString("#.#", CultureInfo.InvariantCulture));
}
and you will get:
10 => 10
10.12 => 10.1
10.234 => 10.2
See: Custom Numeric Format Strings - MSDN
For the comment:
With the provided example works fine, However when used on 0.2 it
returns .2. Is this the only way? –
Use: "0.#,
If you look at the above specified link then for 0 you will find:
Replaces the zero with the corresponding digit if one is present;
otherwise, zero appears in the result string.

CultureInfo in C#

My system is id-ID, using ',' as decimal separator. I have problems with data in en-US, I want to convert it to id-ID and then back to en-US :
List<string> list = new List<string>(){"0.14","223.54"}; // en-US
list[0] = Convert.ToDecimal(list[0], CultureInfo.InvariantCulture).ToString();
MessageBox.Show("list[0] = " + list[0]); //display "0,14", it is ok!
MessageBox.Show("list[1] = " + list[1]); //display "223,54", it is ok!
Now I want to change it back to US culture, I can simply use Replace to change ',' back to '.' as US culture.
MessageBox.Show("list[0] = " + list[0].Replace(",", ".")); //displays "0.14"
But it is not an elegant way to handle it, I wish I can handle it with CultureInfo, so I try the following statements but can't work!
CultureInfo us = new CultureInfo("en-US");
list[0] = Convert.ToDecimal(list[0], us).ToString();
list[1] = Convert.ToDecimal(list[1], us).ToString();
MessageBox.Show("list[0] = " + list[0]); //display "14", the decimal separator is missing!
MessageBox.Show("list[1] = " + list[1]); //display "223,54", nothing changed!
en-US culture uses comma as a number group separator. This a reason of the mistake.
Better approach is to store decimal as decimal. Use required culture when you output data:
List<string> stringList = new List<string>() { "0.14", "223.54" };
List<decimal> list = stringList.Select(Convert.ToDecimal).ToList();
MessageBox.Show(list[0].ToString(CultureInfo.InvariantCulture));
MessageBox.Show(list[0].ToString(new CultureInfo("en-US")));
MessageBox.Show(list[0].ToString(new CultureInfo("id-ID")));

decimal ToString formatting which gives at least 1 digit, no upper limit

How to format a decimal in C# with at least one digit after the decimal point, but not a fixed upper limit if specified more than 1 digit after the decimal point:
5 -> "5.0"
5.1 -> "5.1"
5.122 -> "5.122"
10.235544545 -> "10.235544545"
Use ToString("0.0###########################").
Some notes:,
There are 27 #s in there, as the decimal structure can accommodate precision up to 28 decimal places.
The 0 custom specifier will cause a digit to always be displayed, even if the value is 0.
The # custom specifier only displays a value if the digit is zero and all of the digits to the right/left of that digit (depending on what side of the decimal point you are on) are zero.
You will need to insert as many # after the first 0 to the right of the decimal point to accommodate the length of all the values you will pass to ToString, if you will only have precision to 10 decimal places, then you need nine # (since you have the first decimal place to the right handled by 0)
For more information, see the section of MSDN titled "Custom Numeric Format Strings".
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var a = 5m;
var b = 5.1m;
var c = 5.122m;
var d = 10.235544545m;
var ar = DecToStr.Work(a);
var br = DecToStr.Work(b);
var cr = DecToStr.Work(c);
var dr = DecToStr.Work(d);
Assert.AreEqual(ar, "5.0");
Assert.AreEqual(br, "5.1");
Assert.AreEqual(cr, "5.122");
Assert.AreEqual(dr, "10.235544545");
}
}
public class DecToStr
{
public static string Work(decimal val)
{
if (val * 10 % 10 == 0)
return val.ToString("0.0");
else
return val.ToString();
}
}
Func<decimal, string> FormatDecimal = d => (
d.ToString().Length <= 3 ||
!d.ToString().Contains(".")) ? d.ToString("#.0") : d.ToString()
);

Categories

Resources