Given a string like: "0.123, 0.456" what is the simplest way to parse the two float values into two variables a and b?
I would suggest:
Split on comma (string.Split)
Trim (string.Trim)
Parse with either float.Parse or float.TryParse. (If you want an exception to be thrown if the format is incorrect, go for Parse. If you want to handle parsing failures as part of your normal control flow, use TryParse.)
If the numbers are definitely going to be in that format, explicitly specify CultureInfo.InvariantCulture. Also consider using decimal (or double) instead of float.
Split the string and try parse each trimmed array item.
string[] parts = "0.123, 0.456".Split(new Char [] { ',' });
foreach (string s in parts)
{
if (s.Trim() != "")
{
var result = float.Parse(s.Trim(), CultureInfo.InvariantCulture); // May throw an InvalidCastException
}
}
Related
Hi I want to find if there is any better way to parse the string to Decimal which covers various format
$1.30
£1.50
€2,50
2,50 €
2.500,00 €
I see a lot of examples using culture to convert . & ,. But in my case, I don't have anything to identify the culture.
This display field I get from the client and I need to extract the value.
I tried following (which didn't work for all scenario) but would like to know if we have any best way to handle this.
Decimal.Parse(value,NumberStyles.Currency |
NumberStyles.Number|NumberStyles.AllowThousands |
NumberStyles.AllowTrailingSign | NumberStyles.AllowCurrencySymbol)
I also tried to use Regex to remove the currency sign but unable to convert both 1.8 or 1,8 in one logic.
Well, assuming you always get a valid currency format, and it's only the culture that changes, you could guess which character is used as a decimal point and which is used as a thousands separator by checking which appears the last in the number. Then remove all the thousand separators and parse it like its culture was invariant.
The code would look like the following:
// Replace with your input
var numberString = "2.500,00 €";
// Regex to extract the number part from the string (supports thousands and decimal separators)
// Simple replace of all non numeric and non ',' '.' characters with nothing might suffice as well
// Depends on the input you receive
var regex = new Regex"^[^\\d-]*(-?(?:\\d|(?<=\\d)\\.(?=\\d{3}))+(?:,\\d+)?|-?(?:\\d|(?<=\\d),(?=\\d{3}))+(?:\\.\\d+)?)[^\\d]*$");
char decimalChar;
char thousandsChar;
// Get the numeric part from the string
var numberPart = regex.Match(numberString).Groups[1].Value;
// Try to guess which character is used for decimals and which is used for thousands
if (numberPart.LastIndexOf(',') > numberPart.LastIndexOf('.'))
{
decimalChar = ',';
thousandsChar = '.';
}
else
{
decimalChar = '.';
thousandsChar = ',';
}
// Remove thousands separators as they are not needed for parsing
numberPart = numberPart.Replace(thousandsChar.ToString(), string.Empty);
// Replace decimal separator with the one from InvariantCulture
// This makes sure the decimal parses successfully using InvariantCulture
numberPart = numberPart.Replace(decimalChar.ToString(),
CultureInfo.InvariantCulture.NumberFormat.CurrencyDecimalSeparator);
// Voilá
var result = decimal.Parse(numberPart, NumberStyles.AllowDecimalPoint | NumberStyles.Number, CultureInfo.InvariantCulture);
It does look a bit of complicated for a simple decimal parsing, but I think should do the work for all the input numbers you get or at least the most of them.
If you do this in some sort of loop, you might want to use compiled regex.
The problem here is that in one case . means decimal point but in other it is a thousnads separator. And then you have , as decimal separator. Clearly, it is impossible for the parser to "guess" what is meant, so the only thing you can do is to decide on some rules on how to handle which case.
If you have control over the UI the best approach would be to validate user input and just reject any value that can't be parsed with an explanation on which format is expected.
If you have no control over the UI, the second best option would be to check for some "rules" and then devise which culture is appropriate for that given input and try to run it through decimal.TryParse for that given culture.
For the given input you have, you could have the following rules:
input.StartsWith("$") -> en-US
input.StartsWith("£") -> en-GB
input.StartsWith("€") || input.EndsWith("€") -> de-DE
These could reasonably handle all cases.
In code:
static void Main(string[] args)
{
string[] inputs =
{
"$1.30",
"£1.50",
"€2,50",
"2,50 €",
"2.500,00 €"
};
for (int i = 0; i < inputs.Length; i++)
{
Console.Write((i + 1).ToString() + ". ");
if (decimal.TryParse(inputs[i], NumberStyles.Currency,
GetAppropriateCulture(inputs[i]), out var parsed))
{
Console.WriteLine(parsed);
}
else
{
Console.WriteLine("Can't parse");
}
}
}
private static CultureInfo GetAppropriateCulture(string input)
{
if (input.StartsWith("$"))
return CultureInfo.CreateSpecificCulture("en-US");
if (input.StartsWith("£"))
return CultureInfo.CreateSpecificCulture("en-GB");
if (input.StartsWith("€") || input.EndsWith("€"))
return CultureInfo.CreateSpecificCulture("de-DE");
return CultureInfo.InvariantCulture;
}
Output:
1.30
1.50
2.50
2.50
2500.00
The only way you could do that is just strip string from symbols and change . and , to decimal separator. Something like:
public decimal UniversalConvertDecimal(string str)
{
char currentDecimalSeparator = Convert.ToChar(Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator);
str = str.Replace('.', currentDecimalSeparator);
str = str.Replace(',', currentDecimalSeparator);
StringBuilder builder = new StringBuilder(str.Length);
foreach(var ch in str)
{
if(Char.IsDigit(ch) || ch == currentDecimalSeparator)
builder.Add(ch);
}
string s = builder.ToString();
return Convert.ToDecimal(s);
}
First you have to get current decimal separator from your system.
Then you have to replace . and , with current decimal separator.
Next, you will have to strip the string from any other char than a digit or decimal separator. At the end you can be sure that Convert.ToDecimal is going to work. But I don't know if it is something you want to achieve.
If you need some mechanism to save currency to database, there is a far simpler solution. Just convert this currency to least currency part. For example instead of $1, save 100 cents.
So if you have $1.99, just multiply it by 100 and you will get: 199 cents. And this integer can be saved to db.
I have a date range come like this,
string ActualReleaseDates ="7/8/2016, 7/9/2016, 7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate ="07/11/2016";
I want to check NewsReleaseDate is inside the ActualReleaseDates
But in the following code it return as a false.
if (ActualReleaseDates.Split(',').Contains(NewsReleasedDate.TrimStart(new Char[] { '0' })))
{
//some code here
}
The immediate problem is that after splitting your ActualReleaseDates string, there isn't an entry of "7/11/2016"... instead, there's an entry of " 7/11/2016"... note the space.
But more fundamentally, just trimming the start of NewsReleasedDate won't help if the value is something like "07/08/2016"... what you should be doing is handling these values as dates, rather than as strings:
Split ActualReleaseDates by comma, then parse each value (after trimming whitespace) in an appropriate format (which I suspect is M/d/yyyy) so that you get a List<DateTime>.
Parse NewsReleasedDate in the appropriate format, which I suspect is MM/dd/yyyy, so you get a DateTime.
See whether the parsed value from the second step occurs in the list from the first step.
(I'd personally recommend using Noda Time and parsing to LocalDate values, but I'm biased...)
Fundamentally, you're trying to see whether one date occurs in a list of dates... so make sure you get your data into its most appropriate representation as early as possible. Ideally, avoid using strings for this at all... we don't know where your data has come from, but if it started off in another representation and was converted into text, see if you can avoid that conversion.
The white space problem. You can use trim() and ' 7/11/2016' will be '7/11/2016'
var ActualReleaseDates = "7/8/2016, 7/9/2016, 7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
var NewsReleasedDate = "07/11/2016";
var splitActualReleaseDates = ActualReleaseDates.Split(',').Select(x => x.Trim());
if (splitActualReleaseDates.Contains(NewsReleasedDate.TrimStart(new Char[] { '0' })))
{
}
You can use linq to convert your strings into DateTime objects and compare them instead of strings
string ActualReleaseDates ="7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate ="07/11/2016";
var releaseDates = ActualReleaseDates.Split(',').Select(x => DateTime.Parse(x));
var newsReleased = DateTime.Parse(NewsReleaseDate);
if (releaseDates.Contains(newsReleased))
{
//some code here
}
please note that DateTime is parsed respectively to the current Culture. You can use DateTime.ParseExact if you want to specify exact date format.
You can Prase to DateTime before doing the query like this:
(I think this is the most accurate and guaranteed way to compare dates)
Func<string, DateTime> stringToDate = s => DateTime.ParseExact(s.Trim(), "M/d/yyyy",
CultureInfo.InvariantCulture);
DateTime newReleaseDateTime = stringToDate(NewsReleasedDate);
bool result = ActualReleaseDates.Split(',').Select(x => stringToDate(x))
.Contains(newReleaseDateTime);
It returns false because of the date 07/11/2016 stored in NewsReleasedDate is stored as string with a '0' at the begining. And in the ActualReleaseDates string you have white spaces between the ',' and numbers.
Try to rewrite theese strings like this :
ActualReleaseDates ="7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016"; // white spaces removed.
and the variable like this :
NewsReleasedDate ="7/11/2016"; // 0 removed
This is my code example :
string ActualReleaseDates = "7/8/2016,7/9/2016,7/11/2016,7/3/2016,7/10/2016,7/17/2016,7/24/2016,7/31/2016";
string NewsReleasedDate = "7/11/2016";
string[] dates = ActualReleaseDates.Split(',');
Console.WriteLine(dates.Contains(NewsReleasedDate));
This is not the best way to compare dates, you can use Date class which is usefull to do this kind of comparations.
I am trying to convert a System.string to a System.double, the input is: He: 4.002602 amu
Code:
string[] data = line.Replace(" ", "").Replace("amu", "").Split(new char[] { ':' });
double i = Convert.ToDouble(data[1]);
I have tried:
string[] data = line.Replace(" ", "").Replace("amu", "").Split(new char[] { ':' });
double i = Convert.ToDouble(data[1], CultureInfo.InvariantCulture);
You can use following regular expression without splitting a string which mixed with number and words:
// using System.Text.RegularExpressions
var resultString = Regex.Match(line, #"\d.\d+").Value;
If format of input is something+blank+doubleNumber+blank+something you can use following code:
string line = "He: 4.002602 amu";
int intLspacePos = line.IndexOf(" ") + 1;
int intRspacePos = line.LastIndexOf(" ");
string strNumber = line.Substring(intLspacePos, intRspacePos - intLspacePos);
double dblNumber = Convert.ToDouble(strNumber);
Instead of Convert.ToDouble() use the double.Parse() method, preferably using the invariant culture when the user input is always using a period sign as decimal separator.
So try something like this:
double i = double.Parse(data[1], CultureInfo.InvariantCulture);
EDIT:
I've seen in the screenshot you posted in the comments above that you're not actually checking the contents of data[1] before passing it to the Convert.ToDouble method.
According to MSDN the only case when a FormatException is thrown should be when providing a non-numeric text value (see here). Therefor I'd suggest to add a check for empty strings and null values before, passing the value to the Convert.ToDouble() method. Try updating your code to something like this:
foreach (string line in raw) {
string[] data = line.Replace(" ", "").Replace("amu", "").Split(new char[] { ':' });
if (!string.IsNullOrWhiteSpace(data[1]) {
double i = Convert.ToDouble(data[1], CultureInfo.InvariantCulture);
} else {
// Invalid value in data[1]
// Maybe set a breakpoint here and investigate further if necessary
}
}
If this still throws a FormatException then the contents of data[1] must be some non-numeric and non-empty text value, so in that case you should probably check the contents of the data array using the debugger and find out how / why that invalid value got there.
you can convert with specific culture info as below. 2057 is LCID for English (UK).
double i = Convert.ToDouble(data[1], CultureInfo.GetCultureInfo(2057));
I want to convert string to double.
Here's example of what I do :
string line = "4.1;4.0;4.0;3.8;4.0;4.3;4.2;4.0;";
double[] values = line2.Split(';').Select(double.Parse).ToArray();
But an error appears
Input string was not in a correct format.
When I try
string line2 = "1;2;3;4;5;6;7;8;9;10;11;12";
double[] values = line2.Split(';').Select(double.Parse).ToArray();
It works perfectly fine.
What should be input format for double values to work ?
Your problem is the last semicolon in the first input. The double.Parse method is being passed an empty string. double value2 = double.Parse(""); There are several ways to fix this, I'll outline two here:
Check if the last character in the input is a semicolon, if so, strip it. (This should be self explanatory.)
Use the StringSplitOptions.RemoveEmptyEntries overload.
I prefer the second option, myself. As this also removes issue with two consecutive semicolons.
string line = "4.1;4.0;4.0;3.8;4.0;;4.3;4.2;4.0;";
double[] values = line.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries).Select(double.Parse).ToArray();
Also, just to humour the idea that it could be a culture issue as well; the following code makes adjustments for culture-specific scenarios. The reason for the if check is to save on compute time. It could be removed if you desire, with no harm to the overall affect. (It simply means that the programme will replace . with . in situations where the programme is run on a computer with a culture set to use decimals for separators. This is merely a simple optimization.)
string line = "4.1;4.0;4.0;3.8;4.0;;4.3;4.2;4.0;";
if (CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator != ".")
line = line.Replace(".", CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
double[] values = line.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries).Select(s => double.Parse(s)).ToArray();
Another sidebar about the potential for a culture issue: if it were a culture issue, the operation would not have thrown an exception, but instead simply return each digit of the number without separator. (I.e. 41, 40, 40, 38, 40, 43, 42, 40)
Other options:
Use double.TryParse instead.
Use a custom for loop and manually parse your data.
There are likely other options as well that I cannot think of.
Another option would be to use the double.TryParse() method on each item in your split array. This will ensure that each item in the array (empty or not) is a valid double before attempting to add it to the values array.
For example:
string line = "4.1;4.0;4.0;3.8;4.0;4.3;4.2;4.0;";
double temp = 0;
double[] values = line.Split(';')
.Where(item => double.TryParse(item, out temp))
.Select(i => temp).ToArray();
Is there an easy way to check if a format string is valid? For example the following is code that we use to test a number format string;
public static bool IsValidFormatStringNumber(string FormatString)
{
try
{
const decimal number = 0.056m;
var formattedNumber = number.ToString(FormatString);
return formattedNumber.Length > 0;
}
catch
{
return false;
}
}
We're trying to catch an exception or determine if the resulting string has no length. This test fails however as a format string of "hsibbur" (Any rubbish) results in a string of "hsaibbur", which has length.
We want to do the same test for Percent and Date format string.
If you just want to check for standard format strings, just check that your format strings are part of that list.
If you want to check for custom format strings (that are not "Other" or "Literal strings"), you can probably craft a regex to do it.
Other than that, since format strings can be arbitrary strings, I don't think validation even applies.
If FormatString is equal to formattedNumber, that could be another case for returning false.