testing if a string can be cast as a integer in VB.NET - vb.net

Is there a better way of testing if a string can be converted to an integer other than something like the following?
Public Function IsInt(ByVal value As Object) As Boolean
Try
Dim temp As Integer = CInt(value)
Return True
Catch ex As Exception
Return False
End Try
End Function
by "better" I mean less verbose and/or w/o an exception.
TryParse would be the way to go, but I'm using the compact framework 2.0 and tryparse doesn't seem to be implemented....
Thanks anyways.
It seems that MarkJ is correct and the above seems to be functionally the same as IsNumeric, so I suppose that's my answer. I don't know why I thought CInt was more strict than IsNumeric. I guess it's better to test using CInt verses IsNumeric since that's the function I'm using to do the conversion?

You can use the built in IsNumeric Function
Dim CanConvert as Boolean = IsNumeric(value)
http://msdn.microsoft.com/en-us/library/6cd3f6w1(VS.71).aspx

Since TryParse isn't supported on the Compact Framework, regex is your next best option.
The first example doesn't allow decimals. The second one does.
Regex.IsMatch(value, "^-?\d+$")
Regex.IsMatch(value, "^-?\d+(?:\.\d+)?$")
If you need to allow for scientific notation, you need to tweak it a little more. It really just isn't that bad. You've got the beginning of the string ^, an optional dash -?, one or more digits \d+, a non-capturing group (?:) that looks for a single decimal point \. and one or more digits \d+. Another ? to allow either zero or one instances of the non-capturing group, and then the end of the string $.
Edit:
One thing I didn't think about before: this method is a little imprecise because you could get a really huge number that is numerically a valid integer but can't be converted to an Int32. If that's a possibility, you could constrain the number of characters. Instead of \d+, you could do \d{1,8}, for example.

Public Function IsInt(ByVal value As Object) As Boolean
Dim i As Integer
Return Integer.TryParse(Convert.ToString(value), i)
End Function

you can use Integer.TryParse, which will return a bool indicating whether the conversion was successfull or not

well if you want to avoid using exceptions you could match it against a regular expression that allows only digit characters before converting.

If you're only performing the conversion infrequently, what you have is fine (assuming there's no TryParse() available to you) - it's not going to affect performance.
If you're going to perform millions of conversions, and a large number of them might fail then the exception you're catching could be a perf issue (maybe).
If you can't use TryParse() probably the best thing to do (perf-wise) is to simply check each character in the string and if it's not a digit return false. Don't forget to account for a possible negative sign and group separators (if you want to support them).
Otherwise, parse the string to an int, which will succeed in 99% of the cases. you'll only get an exception if it won't fit. If you really want to avoid the exception that Parse() might generate, it's not hard to actually parse the sting of digits yourself, and return failure if it goes out of range.
Jon Skeet did a quick analysis of this back before the Framework contained TryParse():
Checking if a string can be converted to Int32
None of this fixes the verbosity, though. but as long as it's a self-contained method, there's no real problem with a little verbosity.

Dim s as String = "23"
Dim i as Integer
If Int32.TryParse(s, i) Then
' String was a valid integer... '
End If

Use the TryParse shared method of the Integer type.
For example:
Private Function CanStringBeCastAsInteger(ByVal myString As String) As Boolean
Dim myInt As Integer = 0
Return Integer.TryParse(myString, myInt)
End Function
The benefit of using the TryParse method is that it avoids having to throw and subsequently catch an Exception when the cast fails. Throwing and catching exceptions is an expensive operation.
Not only will the TryParse method return a True/False result, telling you if the conversion will succeed or not, it will also return, in the myInt parameter in my example, the resulting conversion for you, all in one line of code.

Here is something very similar to what you have already but uses the Convert class instead of CType and does not use TryParse
Public Function IsInt(ByVal value As Object) As Boolean
Try
Convert.ToInt32(value)
Return True
Catch ex As System.FormatException
Return False
End Try
End Function

You'd need to roll your own regex e.x.: Regex.IsMatch("4354354", "\d+"), and still include the try/catch block as a backup.

Do as follow
If myString <> "" And IsNumeric(myString) = True Then
If CDbl(myString) - Int(CDbl(myString)) > 0 Then
.... myString is integer
End If
End If

Related

IsNumeric() allowing minus at last of the values

I am using IsNumeric() function in my code to validate numbers.
IsNumeric(100) - true,
IsNumeric(-100) - true,
IsNumeric(+100) - true,
IsNumeric(100-) - true - I have doubt in this. (100-) Is this a valid number? IsNumeric () returns true to this value.
Dim latitude As String = "12.56346-"
If IsNumeric(latitude) Then
If (Convert.ToDouble(latitude) >= -90 And Convert.ToDouble(latitude) <= 90) Then
isValidLatitude.Text = "true"
Else
isValidLatitude.Text = "false"
End If
Else
isValidLatitude.Text = "false"
End If
Error while converting latitude to double
Input string was not in a correct format.
IsNumeric is from the Microsoft.VisualBasic namespace/dll, a bunch of helper stuff intended to help VB6 programmers get their arcane VB6 code /knowledge working on VB.NET
You'll note if you use other functions from the same dll such as Microsoft.VisualBasic.Conversion.Int(), or you use dedicated VB.NET converters such as CInt or CDbl these will also cope with a trailing minus sign and return a negative value
If you want to dispense with the old ways of VB6, use a numeric type's TryParse.. but all in, be consistent / if you use a function from Microsoft.VisualBasic to determine if a conversion can be made, use the vb conversion because within itself the package will be consistent but between Microsoft.VB and normal .net System there are some differences in behavior
Edit:
A couple of people have been wondering about the source code and how it uses TryParse, so why doesn't it work like using TryParse directly?
Microsoft.VisualBasic.Information.IsNumeric() uses Microsoft.VisualBasic.CompilerServices.DoubleType.TryParse() to determine whether an expression is numeric. This DoubleType.TryParse is not the same as Double.TryParse - it is a helper method again in the VB namespace that specifically sets the NumberStyles.AllowTrailingSign flag among many other flags. When parsing the number (first as non currency related, using Double.Parse, then if it fails a second attempt is made using adjusted values for currency related tests) this AllowTrailingSign flag will be considered in conjunction with other regional number formatting rules in determining if the passed in value is numeric
You'll note that on a machine obeying a USA number formatting culture, a string of "(100$)" is also declared to be numeric but calling Double.TryParse("(100$)", x) will also return false. The VB helper methods here are being a lot more liberal in what they accept than the System methods, because they're telling the System methods to be more liberal than they are by default
As noted, I've always regarded the Microsoft.VisualBasic namespace as a bunch of helper methods intended to allow terrible old VB6 code to be pasted into VB.NET and work with minimal fiddling. I wouldn't advocate using it for new projects and I remove the reference when I work on VB.NET - using it to support the VB6 notions of "just sling anything in, of any type, and it'll probably figure it out and work.. and if it doesn't we can always on error resume next" should be discarded in favour of precise and accurate about the operations executed and their intent
Note: my previous answers were wrong about assuming it was a bug. As the answer of #Damien_The_Unbeliever states, this function tries to validate the string as a lot of data types. And actually, the value "100-" is a valid Decimal number. That's why it returns true (as it's a "valid" Decimal) but gives a exception when converting to Double (as it's not a valid Double). #Damien_The_Unbeliever really deserves your +1 for pointing that.
From the documentation (showing all the data types that IsNumeric tries to validate):
IsNumeric returns True if the data type of Expression is Boolean, Byte, Decimal, Double, Integer, Long, SByte, Short, Single, UInteger, ULong, or UShort. It also returns True if Expression is a Char, String, or Object that can be successfully converted to a number. Expression can contain non-numeric characters. IsNumeric returns True if Expression is a string that contains a valid hexadecimal or octal number. IsNumeric also returns True if Expression contains a valid numeric expression that begins with a + or - character or contains commas.
Also, #CaiusJard did a nice search and pointed out that inner methods use a NumberStyles.AllowTrailingSign option, which allows this behavior.
Ok, now to the solution:
Just use a TryParse method, from your desired data type (int, long, etc...). The cool thing is that it'll behaves exactly as you expect, and if the parsing is successful, we have the parsed value already available to use!
if (Int32.TryParse(value, out int number))
{
// String is a valid number, and is already parsed in the 'number' variable!
}
else
{
// String is not a valid number!
}
Solution's VB.Net version:
Dim value As String = "12.56346-"
Dim number As Double = 0
If Double.TryParse(value, number) Then
' String is a valid number, and is already parsed in the "number" variable!
isValidLatitude.Text = "true"
Else
' String is not a valid number!
isValidLatitude.Text = "false"
End If
IsNumeric answers a question no sane person wants to ask. As quoted by Vitox's answer:
IsNumeric returns True if the data type of Expression is Boolean, Byte, Decimal, Double, Integer, Long, SByte, Short, Single, UInteger, ULong, or UShort. It also returns True if Expression is a Char, String, or Object that can be successfully converted to a number.
Note, it doesn't tell you that the given string can be converted to all numeric types. It tells you that the string can be converted to at least one numeric type. And for bonus bad style points, of course, it doesn't tell you which types the string can be converted to.
Decimal.Parse("100-") will execute perfectly well and give you a Decimal containing a value of -100.
So, it's not a bug, it's a bad function that has been retained for backwards compatibility reasons. Nowadays, we know better, and that we want to test whether a string can be converted to a specific data type, for which the TryParse family of functions have been designed.

CDec doesn't return the correct value

I make code that multiply all values of a DatagridView column. The code is working, but there's a problem. In particular, I've added this:
Return CDec(x.Cells("Quote").Value.ToString.Replace(",", "."))
That returns me the split value, for example, if I would have:
2,7 the correct value to return is: 2.7, but the code returns 27 and this is strange. I performed some trying, and if I delete the CDec like:
Return x.Cells("Quote").Value.ToString.Replace(",", ".")
The value returned is correct, but I've a contrast with this function:
Private Function MultiplyDecimals(ByVal sender As Decimal()) As Decimal
Dim Result As Decimal
If Not sender Is Nothing AndAlso Not sender.Length = 0 Then
Result = sender.Aggregate(Function(a, b) a * b)
End If
Return Result
End Function
That returns me 0 if I delete "As Decimal". So what's wrong?
UPDATE with possible solution:
Dim bles = x.Cells("Quote").Value.ToString.Replace(",", ".")
Dim key = Convert.ToDecimal(bles)
MessageBox.Show(key)
Return CDec(key)
Since it looks like you are also trying to manually convert from a different culture, you should use the .NET methods for this, rather than roll your own. You have two conversions going on: From one cultural number system to another and from string to a numeric type.
To convert a string like "2,7" which might be a French or Italian value:
Dim str As String = "2,7"
Dim decVal As Decimal
If Decimal.TryParse(str, NumberStyles.AllowDecimalPoint,
New CultureInfo("it-IT"), decVal) Then
' decVal has the value
Else
' Parse failed
End If
or:
Dim cult As New CultureInfo("fr-FR")
If Decimal.TryParse(str, NumberStyles.AllowDecimalPoint,
cult, decVal) Then ...
This can often work, but you might get an exception depending on the content:
Dim decVal As Decimal
Dim cult As New CultureInfo("fr-FR")
decVal = Convert.ToDecimal(str, cult)
Console.WriteLine(decVal.ToString)
Note: I am guessing at the culture of the data. Based on your recent, related question Google indicates "Nazione" is Italian, but data from/about Algeria might well be French Algerian.
CDec, like most of the legacy VB functions, is only equipped to work with the active culture settings. So, it will seem like it is "broken" in some cases:
Console.WriteLine(CDec("2.7")) ' --> 2.7D US/Can
Console.WriteLine(CDec("2,7")) ' --> 27D
Console.WriteLine(CDec("2,,7")) ' --> 27D
On a machine using French or Italian culture, the opposite will happen: "2.7" will come out 27, "2,7" to 2.7D, but "2,,7" will probably crash just like "2..7" would in the US/Can.
The reason for this behavior is that in US/Can, a comma looks like noise and CDec ignores it. A string with a single decimal-dot will convert, but a string with 2 is invalid. It would be the reverse for French where the comma is used: any number of dots look like noise, and only single decimal-comma format will parse.
It is all part of the reason no one included CDec in the solution: It is sub-optimal for any sort of cultural conversions. Manually removing or replacing decimal markers is even less optimal when there are methods designed to do that only when needed.
Since you have a string value, try Decimal.Parse() instead of CDec().
If that doesn't give you the result you expect, then either you're not handling the string that you think you are (in which case, try logging the exact string to a text file or similar so you can see exactly what you have), or the PC's culture settings are not what you think they are (it may be set to a culture that uses the comma, rather than a period, as the decimal separator), in which case try one of the Decimal.Parse() overloads that allows you to specify the culture, or perhaps even just remove the call to .Replace().

Convert String to int16

I faced a "ridiculous" problem.
I was trying to convert a string to int16 (I am forced to do it in int16 and not in int32/integer).
My first thought was to try:
convertedVal = Convert.ToInt16(newVal)
which thrown an exception: Value was either too large or too small for UInt16.
But my string was "10", so it was between the minValue and the maxValue.
I solved my problem using :
convertedVal = Int16.Parse(newVal) 'TryParse works also
Although I solved my problem I haven't understand what I did wrong.
Could somebody explain to me why this happened?
Thanks for your time
This usually happens if there is an extra space on the string, so better Trim it
convertedVal = Convert.ToInt16(newVal.Trim())
Both methods should be the same according to the MSDN page
Using the ToInt16(String) method is equivalent to passing value to the Int16.Parse(String) method. value is interpreted by using the formatting conventions of the current thread culture.
Where do you get this string?
If it comes from user input I wouldn't trust much that he/she types a correct int16 value to use a Convert or Parse method.
using TryParse leads to a more robust code.
Dim result as Short
Dim newVal as String = "10 sadaas"
if Int16.TryParse(newVal, result) = False then
result = 0
end if

CStr() vs. Str() vs. .ToString()

I want to know what exactly are the differences between CStr(), Str() and .ToString()?
Label1.Text = CStr(Int(Rnd() * 10))
and
Label1.Text = Str(Int(Rnd() * 10))
and
Label1.Text = Int(Rnd() * 10).ToString
If I use this condition:
If Label1.Text = "7" Then
'Some code here
End If
Str() doesn't work here. What's the difference?
ToString will call the .ToString() function on a particular instance.
In practice, this means that it will throw an exception if the object in
question is Nothing. However, you can implement .ToString() in your own
classes to get a useful string representation of your object, whereas
CType/CStr only work with built-in classes and interfaces.
CStr and CType(expression, String) are exactly equivalent (I'm not
sure where the other poster got the idea that CStr is faster). But they
aren't really functions, they're compiler directives that will emit very
different code depending on the declaration of expression. In most
cases, these directives call a bunch of internal VB code that tries to
get a reasonable string out of expression.
DirectCast(expression, String) assumes that the expression in
question really is a String and just casts it. It's the fastest of all
these options, but will throw an exception if expression is anything
other than a String.
As an Addition to the VBA/VB6 Environment where we have no ToString():
Str() is not aware of international representation. The decimal separator always is a dot (.).
As already mentioned above it prefixes the resulting string with a blank in case of positive values.
There also exists Str$(). The difference to Str() is the return type:
Str() returns a variant of type string, Str$() returns a string.
And Str$() is slightly faster then Str().
CStr() in contrast is aware of international representation. The decimal separator depends on the Windows international settings.
No additional prefixing for positive values will be done.
So if you need to convert a value type to a string and have to ensure a dot as a decimal separator and no prefixing blank, then use this syntax:
Dim d As Double
d = 123.456
Dim s As String
s = Trim(Str$(d))
I don't know about ToString() and i don't know about VB.NET
But in VB6 (Visual Basic 6):
Both of Cstr() and Str() converts values to string. but Cstr() is better because:
Str(): After converting to string it adds 1 space before positive numbers. for example: Str(22) > " 22"
Cstr(): After converting to string it never adds the above extra space - For best result use it with Trim() - Trim(Cstr(Variable))
Although not a problem in the code in the question, it is important to mention that Str() only converts numerical expressions to string, gives an error in other cases, so don't use it for converting values of a cell.
My answer is str() is evil as it always prepends a space for the sign character so if you are comparing values it fails. Instead use CStr() instead which does not do this.
You may comes across business logic that tries to do this:
Eg:
Dim sVar as String = "1"
Dim i as Integer = 1
console.write( cstr(i) = sVar )
Which outputs:
False
I lost a couple hours on this one as the code was quite deep in old code and was very difficult to grok in production environment where logging based debugging was all that was available.

Check for only digits in VB.NET String

I want to run a check on a String right before I append it to a StringBuilder to make sure only numeric characters are in the string. What's a simple way to do that?
Use Integer.TryParse() it will return true if there are only digits in the string. Int32 max value is 2,147,483,647 so if your value is less then that then your fine.
http://msdn.microsoft.com/en-us/library/f02979c7.aspx
You can also use Double.TryParse() which has a max value of 1.7976931348623157E+308 but it will allow a decimal point.
If your looking to get the value that isnt an integer you can always go through the string one at a time
string test = "1112003212g1232";
int result;
bool append=true;
for (int i = 0; i < test.Length-1; i++)
{
if(!Int32.TryParse(test.Substring(i,i+1),out result))
{
//Not an integer
append = false;
}
}
If append stays true then the string is an integer. Probably a more slick way of doing this but this should work.
Coding in VB.Net to check whether a string contains only numeric values or not.
If IsNumeric("your_text") Then
MessageBox.Show("yes")
Else
MessageBox.Show("no")
End If
Use regular expressions:
Dim reg as New RegEx("^\d$")
If reg.IsMatch(myStringToTest) Then
' Numeric
Else
' Not
End If
UPDATE:
You could also use linq to accomplish the same task if you're doing it in VB.Net 2008/2010.
Dim isNumeric as Boolean = False
Dim stringQuery = From c In myStringToTest
Where Char.IsDigit(c)
Select c
If stringQuery.Count <> myStringToTest.Length Then isNumeric = False
If you do not wish to use RegEx, a simple check on each character with char.IsNumber works.
You can combine it with the All extension method (in C#, I don't know how to write it in VB.net):
string value = "78645655";
bool isValid = value.All(char.IsNumber);
Check out other char method, like IsDigit.
2 other compact solutions :
Without LINQ :
Dim foo As String = "10004"
Array.Exists(foo.ToCharArray, Function(c As Char) Not Char.IsNumber(c))
With LINQ (just VB.Net equivalent of the C# version in another answer) :
foo.All(Function(c As Char) Char.IsNumber(c))
Negative values haven't been mentioned, which Integer.TryParse would accept.
I prefer UInteger.TryParse which will reject a negative number. However, there is an additional check required in case the value starts with "+":
Dim test As String = "1444"
Dim outTest As UInteger
If Not test.StartsWith("+") AndAlso UInteger.TryParse(test, outTest) Then
MessageBox.Show("It's just digits!")
End If
or ULong.TryParse for a larger number.
Pattern matching! See this, this (about.com) and this (VB.NET dev article).
You can use regular expression or Integer.TryParse and I prefer the regular expression check
Presuming you're looking at relatively short strings which will never have a number greater than the Max Int32 value, use Gage's solution. If it's a variable length and sometimes you could overflow, use Regex (System.Text.RegularExpressions)
The regex for checking against just numbers is fairly routine: ^[0-9]+$
Check here for a very good explanation of Regex.