How to parse number with multiple possible thousandsseparators? - c#

Culture nb-NO has " " (space) as thousands separator (numbergroupseparator, currencygroupseparator etc), but it's very common to also type "." (period).
Decimal separator is ",".
How can I assign multiple thousands separators?

If you know what the separator is or know a list, use Int.TryParse and pass in the different cultures representing the separators. For example:
using System.Globalization;
...
string number; // Contains number to parse
int parsedNumber;
List<CultureInfo> cultures = new List<CultureInfo>()
{
Cultureinfo.InvariantCulture,
new CultureInfo("en-US"),
... // Insert other cultures here
};
CultureInfo matchingCulture = cultures.FirstOrDefault(cultureInfo =>
Int.TryParse(number, out parsedNumber,
NumberStyles.AllowThousands, cultureInfo));
if(matchingCulture != null)
{
// parsedNumber contains the parsed number and matchingCulture contains
// the culture that parsed it
}
If separators are not represented by different cultures, consider using a simple string.Replace() to replace a list of known separators with a single known one, such as a comma. Note that this may have issues, for example, where thousands separators are used with decimal points because some country's conventions conflict.

If you can't find a built-in solution, you can always replace the possible unwanted characters such as , with empty string, then parse the number.

Related

C# Formatting numbers with spaces delimiting thousands

I'm trying to format a decimal with the following requirements:
Thousands are separated by spaces " "
Decimal point is delimited by a
comma "," (this is achieved by using the appropriate culture, in this
case Croatian)
There are two digits after the decimal point.
So far I got this:
String.Format(new CultureInfo("hr-HR"), "{0:# ##0.00}", input)
This works well if the number has 4 or more digits before the decimal point. For example the value 5500.5 gives me "5 500,50" and -5500.5 gives me "-5 500,50", which is what I want.
But if the number has fewer digits, I get a white space in front of the number. For example 500.5 gives me " 500,50" instead of "500,50". And with a negative number, the space is put between the minus sign and the digits: -500.5 gives me "- 500,50". So I can't simply trim the result. How can I achieve what I need?
You can use a custom NumberFormatInfo to format numbers.
var nfi = new NumberFormatInfo();
nfi.NumberGroupSeparator = " "; // set the group separator to a space
nfi.NumberDecimalSeparator = ","; // set decimal separator to comma
And then format the number using
number.ToString("N2", nfi); // numeric format with 2 decimal digits
This gives you
(5500.5).ToString("N2", nfi) // "5 500,50"
(-500.5).ToString("N2", nfi) // "-500,50"
(-5500.5).ToString("N2", nfi) // "-5 500,50"
The simplest approach may be to just check if input is over a 1000 / less than -1000 before formatting it this way.
if(input >= 1000 || input <= -1000){
// use String.Format(new CultureInfo("hr-HR"), "{0:# ##0.00}", input)
}else{
// just use input.ToString()?
}
There's probably a way to coerce string.format into doing what you want, but why overcomplicate the issue?

C#: Exponential Format Specifier

I have a double number:
element.MaxAllowableConcLimitPpm = 0.077724795640326971;
I need to display it as
7.7725e-2
when I try to use it:
element.MaxAllowableConcLimitPpm.ToString("e4", CultureInfo.InvariantCulture)
it returns
7.7725e-002
How to say that mantissa should have one symbol instead of 3 ?
Format like this:
.ToString("0.0000e0")
returns
5.0000e2
instead of
5.0000e+2
You have to use a custom numeric format string - standard numeric format strings always have at least three digits in the exponent.
Example with a custom string:
using System;
public class Test
{
static void Main()
{
double value = 0.077724795640326971;
Console.WriteLine(value.ToString("0.0000e+0")); // 7.7725e-2
}
}
From the documentation for standard numeric format strings (emphasis mine):
The case of the format specifier indicates whether to prefix the exponent with an "E" or an "e". The exponent always consists of a plus or minus sign and a minimum of three digits. The exponent is padded with zeros to meet this minimum, if required.

How to add thousands separator to string value which may contain floating points

I have a string series of values which may or may not contain a floating point number. I want to add the thousands separator to this numeric string. I want to have the value with thousands separators and floating point number only when it's there. How can I do this?
Examples:
Input: 23456.78
Output: 23,456.78
Input: 23456
Output: 23,456
Try parsing to decimal (or double) and then format back to the required representation ("#,#.##########" format string in your case):
String input = "23456.78";
// 23,456.78
String output = decimal
.Parse(input, CultureInfo.InvariantCulture)
.ToString("#,#.##########", CultureInfo.InvariantCulture);

Formatting long datetime string to remove T character

I have a number of XML nodes which output a datetime object as string.
The problem is that when outputting both the time stamp and the date they are bonded together with a T Character.
Here is an example
2016-01-13T23:59:59
Of course all of the nodes in the XML are of a different type so grouping by name or type is out of the question. Im thinking my only option is to match a pattern with regex and resolve the problem that way.
Below is an example of how the XML would work, you can see that each element is named as something different but they all follow a similar pattern, where the T between the date and the time must be removed and a space replaced instead.
<dates>
<1stDate> 2016-01-13T23:59:59 </1stdate>
<2ndDate> 2017-01-13T23:55:57 </2ndDate>
<3rdDate> 2018-01-13T23:22:19 </3rdDate>
</dates>
Ideal solution to output like this
2016-01-13 23:59:59
2017-01-13 23:55:57
2018-01-13 23:22:19
I havent had to use Regex before but i know what it is. I have been trying to decode what this cheat sheet means http://regexlib.com/CheatSheet.aspx?AspxAutoDetectCookieSupport=1 but to no avail.
UPDATE
//How each node is output
foreach (XText node in nodes)
{
node.Value = node.Value.Replace("T"," "); // Where a date occurs, replace T with space.
}
The <date> elements provided in the example may contain dates in my XML but may not include the word date as a name.
e.g.
<Start> 2017-01-13T23:55:57 </start>
<End> 2018-01-13T23:22:19 </End>
<FirstDate> 2018-01-13T23:22:19 </FirstDate>
The main reason I would have liked a regex solution was because I need to match the date string with a pattern that can determine if its a date or not, then i can apply formatting.
Why not parse that (perfectly valid ISO-8601) date time into a DateTime, and then use the built in string formatting to produce a presentable human readable date time?
if (!string.IsNullOrWhiteSpace(node.Value))
{
DateTime date;
if (DateTime.TryParseExact(node.Value.Trim(),
#"yyyy-MM-dd\THH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal,
out date)
{
node.Value = date.ToString("yyyy-MM-dd HH:mm:ss");
}
}
I would use:
if (DateTime.TryParse(yourString))
{
yourString.Replace("T", " ");
}
EDIT
If you would only like to replace the first instance of the letter "T" like I think you are suggesting in your UPDATE. You could use this extension method:
public static string ReplaceFirst(this string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
and you would use it like:
yourString.ReplaceFirst("T", " ");
If you still want to do this with regex, the following expression should do the trick:
# Positive lookbehind for date part which consists of numbers and dashes
(?<=[0-9-]+)
# Match the T in between
T
# Positive lookahead for time part which consists of numbers and colons
(?=[0-9:]+)
EDIT
The regex above will NOT check if the string is in date/time format. It is a generic pattern. To impose the format for your strings use this pattern:
# Positive lookbehind for date part
(?<=\d{4}(-\d{2}){2})
# Match the T
T
# Positive lookahead for time part
(?=\d{2}(:\d{2}){2})
Again, this will match the exactly the strings you have but it you should not use it to validate date/time values because it will match invalid dates like 2015-15-10T24:12:10; to validate date/time values use DateTime.Parse() or DateTime.TryParse() methods.

Can someone help me out with some regex?

I'm trying to get an int[] from the following string. I was initially doing a Regex().Split() but as you can see there isn't a definite way to split this string. I could split on [a-z] but then I have to remove the commas afterwards. It's late and I can't think of a nice way to do this.
"21,false,false,25,false,false,27,false,false,false,false,false,false,false,false"
Any suggestions?
Split on the following:
[^\d]+
You may get an empty element at the beginning if your string doesn't begin with a number and/or an empty element at the end if your string doesn't end with a number. If this happens you can trim those elements. Better yet, if Regex().Split() supports some kind of flag to not return empty strings, use that.
A simple solution here is to match the string with the pattern \d+.
Sometimes it is easier to split by the values you don't want to match, but here it serves as an anti-pattern.:
MatchCollection numbers = Regex.Matches(s, #"\d+");
Another solution is to use regular String.Split and some LINQ magic. First we split by commas, and then check that all letters in a given token are digits:
var numbers = s.Split(',').Where(word => word.All(Char.IsDigit));
It is an commonly ignored fact the \d in .Net matches all Unicode digits, and not just [0-9] (try ١٢٣ in your array, just for fun). A more robust solution is to try to parse each token according to your defined culture, and return the valid numbers. This can easily be adapted to support decimals, exponents or ethnic numeric formats:
static IEnumerable<int> GetIntegers(string s)
{
CultureInfo culture = CultureInfo.InvariantCulture;
string[] tokens = s.Split(',');
foreach (string token in tokens)
{
int number;
if (Int32.TryParse(token, NumberStyles.Integer, culture, out number))
yield return number;
}
}

Categories