I am trying to format a decimal (as a percentage without the % sign), displaying brackets () around negative numbers and displaying positive numbers as is.
var returnString = value.HasValue
? string.Format("{0:0.0;(0.0)}", value.Value * 100) :
DefaultEmptyString;
return returnString;
My main problem is that for a numbers such as -0.000491 which in this formatting scheme is a zero it should still show the brackets as it is a negative zero.
Nonetheless, it is being lost.
I could stop using the section separator and utilize an if statement instead but it seems like the section separator should do the trick.
Any ideas why the formatting is not done correctly ?
Any ideas why the formatting is not done correctly ?
Is is done correctly according to the documentation:
If the number to be formatted is negative, but becomes zero after rounding according to the format in the second section, the resulting zero is formatted according to the first section.
Since I don't see any options available that would override that default behavior, one option is to append the parentheses manually:
public string Format(decimal value)
{
string s = (value*100).ToString("0.0;0.0");
if(value < 0)
s = "(" + s + ")";
return s;
}
From the documentation: http://msdn.microsoft.com/en-US/library/0c899ak8%28v=vs.80%29.aspx
"If the number to be formatted is negative, but becomes zero after rounding according to the format in the second section, then the resulting zero is formatted according to the first section."
The formatting is correct, you will need the if statement.
Related
I am attempting to validate a date text box to make sure the correct date format was entered.
I converted this vb6 code:
If (IsDate(txtBirthDate)) And (IsNumeric(Right(txtBirthDate, 4))))
into this C# code -
int output = 1;
DateTime output2;
if ((! DateTime.TryParse(txtBirthDate.Text, out output2)) & (!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 5)), out output)))
{
MessageBox.Show("error")
}
What I am attempting to do is make sure that the last 4 digits of the date text box are numeric (the year - i.e 1990 in 05/10/1990) and if it is not a number, then show error. Although I cannot verify everything is numeric due to the "/" in the date format.
The code does not show an error and builds. But when I debug the application I receive an error. The error states:
Index and length must refer to a location within the string.
Parameter name: length.
Any ideas on how to accomplish this?
To check if a date is in a specific format, use DateTime.TryParseExact():
if (!DateTime.TryParseExact(output , "d/M/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out output2))
{
MessageBox.Show("error")
}
EDIT: Change the format according to your needs:
"d/M/yyyy" for UK and "M/d/yyyy" for US
Edit: It sounds like the cause of your error is your string is too short. Test the string length before you test the last 4 characters.
Three other issues:
The And operator in C# is &&. You are using & which is a bitwise operator.
To check the last four characters of the string, you should use .Length - 4, not 5.
You are negating the return values in your C#, but not in your VB. To match the VB, omit the !. But, it looks like that's not what you are actually trying to do. It looks like you want to show an error message if either the string is not parsable as a date or the year portion is not 4 digits. In that case use a an or comparison (||):
if (!DateTime.TryParse(txtBirthDate.Text, out output2) ||
txtBirthDate.Text.Length < 4 ||
!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 4)), out output))
{
MessageBox.Show("error")
}
Keep in mind that yyyy/M/d is also parsable as a Date, contains a 4-digit year, but will fail your test. Is that what you want?
From the error provided it looks like you just need a length check to make sure you can process the code. You should also use && instead of & (or in this case probably ||) to ensure that the boolean expressions stop executing once a true state has been encountered.
if (txtBirthDate.Text.Length < 5 ||
(!DateTime.TryParse(txtBirthDate.Text, out output2) ||
!int.TryParse((txtBirthDate.Text.Substring(txtBirthDate.Text.Length - 5)), out output)))
{
MessageBox.Show("error")
}
However, this might be a good case for the use of a regular expression.
Regex reg = new Regex("^\d{2}/\d{2}/\d{4}$");
if (!reg.IsMatch(txtBirthDate.Text))
MessageBox.Show("error");
Tweaking of the regular expression to match fringe cases (leading zero's, alternate formats, etc) may be necessary.
int result;
if (int.TryParse(txtBirthDate.Text.Substring(test.LastIndexOf("/")), out result))
{
//its good
}
else
{
//its bad
}
I am currently using ncalc library to do several evaluation and get the result out of it.
Right now I have found a problem where if I have a price in the format "1,234.01" it will fail to evaluate my expression.
The current workaround I've used was to remove the , but I was wondering if there is way to evaluate a currency without having to remove the , for example:
decimal price = 0;
if (!decimal.TryParse(iPrice.Text, out price))
{
MessageBox.Show("Price is not formatted correctly...");
return;
}
decimal currency = 0;
if (!decimal.TryParse(iCurrency.Text, out currency))
{
MessageBox.Show("Currency is not formatted correctly...");
return;
}
string formula = iFormula.Text.Replace("Price", price.ToString("n2")).Replace("Currency", currency.ToString("n2"));
Expression exp = new Expression(formula);
exp.Evaluate();
Evaluate fails because of the , from my price where if I remove it, it works just fine.
Sample of the formula:
(((Price+12,9)+((Price+12,9)*0,05)+(((Price+12,9)+((Price+12,9)*0,05))*0,029)+0,45)*Currency)
Stacktrace as requested:
NCalc.EvaluationException was unhandled
Message=mismatched input ',' expecting ')' at line 1:4
mismatched input ',' expecting ')' at line 1:20
mismatched input ',' expecting ')' at line 1:43
mismatched input ',' expecting ')' at line 1:59
missing EOF at ')' at line 1:77
Source=NCalc
StackTrace:
at NCalc.Expression.Evaluate()
Your question is still unclear to me, but I suspect you can fix this just by changing the format you're using when replacing. Change this:
string formula = iFormula.Text.Replace("Price", price.ToString("n2"))
.Replace("Currency", currency.ToString("n2"));
to this:
string formula = iFormula.Text.Replace("Price", price.ToString("f2"))
.Replace("Currency", currency.ToString("f2"));
That will use the "fixed point" format instead of the "number" format. You won't get grouping. Note that grouping isn't part of the number itself - it's part of how you format a number.
I'd also be tempted to specify the invariant culture explicitly, by the way.
As an aside: I haven't used NCalc myself, but if it's really forcing you to specify the numeric values in an expression as text, that sounds pretty poor. I'd expect some sort of parameterization (as per most SQL providers, for example) which should make all of this go away.
No, you cannot have a separator in your decimal literal. The compiler will confuse it with declaring multiple variables with the same type like:
decimal price = 1m, tax = 234m;
If it was a string, however, you could parse it like:
decimal price = Decimal.Parse("1,234.0", CultureInfo.InvariantCulture);
EDIT: my answer above was directed to the code sample in the first version of the question. Now that the question has been edited:
You can control the string representation of your decimal values using the Decimal.ToString(string format, IFormatProvider provider) method overload. This allows you to specify a standard or custom format string. In your case, it sounds like you need to have 2 decimal digits separated using a dot, and no group separators (no commas). So you could say:
price.ToString("F2", CultureInfo.InvariantCulture) // ex. result: "1234.56"
CultureInfo.InvariantCulture is important if you need a dot separator regardless of the current culture. If you don't specify that, the output could be "1234,56" depending on the current culture (e.g. in case of european cultures like de-DE, or fr-FR).
I want to format an integer i (-100 < i < 100), such that:
-99 formats as "-99"
9 formats as "+09"
-1 formats as "-01"
0 formats as "+00"
i.ToString("00")
is close but does not add the + sign when the int is positive.
Is there any way to do this without explicit distinguishing between
i >= 0 and i < 0?
Try this:
i.ToString("+00;-00;+00");
When separated by a semicolon (;) the first section will apply to positive values, the second section will apply to negative values, the third section will apply to zero (0).
Note that the third section can be omitted if you want zero to be formatted the same way as positive numbers. The second section can also be omitted if you want negatives formatted the same as positives, but want a different format for zero.
Reference: MSDN Custom Numeric Format Strings: The ";" Section Separator
You might be able to do it with a format string like so..
i.ToString("+00;-00");
This would produce the following output..
2.ToString("+00;-00"); // +02
(-2).ToString("+00;-00"); // -02
0.ToString("+00;-00"); // +00
Take a look at the MSDN documentation for Custom Numeric Format Strings
Try something like this:
i.ToString("+00;-00");
Some examples:
Console.WriteLine((-99).ToString("+00;-00")); // -99
Console.WriteLine(9.ToString("+00;-00")); // +09
Console.WriteLine((-1).ToString("+00;-00")); // -01
Console.WriteLine((0).ToString("+00;-00")); // +00
How do I prevent the code below from throwing a FormatException. I'd like to be able to parse strings with a leading zero into ints. Is there a clean way to do this?
string value = "01";
int i = int.Parse(value);
Your code runs for me, without a FormatException (once you capitalize the method properly):
string value = "01";
int i = int.Parse(value);
But this does ring an old bell; a problem I had years ago, which Microsoft accepted as a bug against localization components of Windows (not .NET). To test whether you're seeing this, run this code and let us know whether you get a FormatException:
string value = "0"; // just a zero
int i = int.Parse(value);
EDIT: here's my post from Usenet, from back in 2007. See if the symptoms match yours.
For reference, here's what we found.
The affected machine had bad data for
the registry value
[HKEY_CURRENT_USER\Control Panel
\International\sPositiveSign].
Normally, this value is an empty
REG_SZ (null-terminated string). In
this case, the string was missing its
terminator. This confused the API
function GetLocaleInfoW(), causing it
to think that '0' (ASCII number zero)
was the positive sign for the current
locale (it should normally be '+').
This caused all kinds of havoc.
You can verify this for yourself with
regedit.exe: open that reg value by
right-clicking on the value and
selecting 'Modify Binary Data'. You
should see two dots on the right
(representing the null terminator).
If you see no dots, you're affected.
Fix it by adding a terminator (four
zeros).
You can also check the value of
CultureInfo.CurrentCulture.NumberFormat.PositiveSign;
it should be '+'.
It's a bug in the Windows localization
API, not the class libs. The reg
value needs to be checked for a
terminator. They're looking at it.
...and here's a report on Microsoft Connect about the issue:
Try
int i = Int32.Parse(value, NumberStyles.Any);
int i = int.parse(value.TrimStart('0'));
TryParse will allow you to confirm the result of the parse without throwing an exception. To quote MSDN
Converts the string representation of
a number to its 32-bit signed integer
equivalent. A return value indicates
whether the operation succeeded.
To use their example
private static void TryToParse(string value)
{
int number;
bool result = Int32.TryParse(value, out number);
if (result)
{
Console.WriteLine("Converted '{0}' to {1}.", value, number);
}
else
{
if (value == null) value = "";
Console.WriteLine("Attempted conversion of '{0}' failed.", value);
}
}
You don't have to do anything at all. Adding leading zeroes does not cause a FormatException.
To be 100% sure I tried your code, and after correcting parse to Parse it runs just fine and doesn't throw any exception.
Obviously you are not showing actual code that you are using, so it's impossible to say where the problem is, but it's definitely not a problem for the Parse method to handle leading zeroes.
Try
int i = Convert.ToInt32(value);
Edit: Hmm. As pointed out, it's just wrapping Int32.Parse. Not sure why you're getting the FormatException, regardless.
I have the following codes that may be helpful:
int i = int.Parse(value.Trim().Length > 1 ?
value.TrimStart(new char[] {'0'}) : value.Trim());
This will trim off all extra leading 0s and avoid the case of only one 0.
For a decimal number:
Convert.ToInt32("01", 10);
// 1
For a 16 bit numbers, where leading zeros are common:
Convert.ToInt32("00000000ff", 16);
// 255
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(",", ".");