input validation in vb 2013 input box string conversion error - vb.net

My program crashes when I enter letter characters into the input box or leave the input box blank. Why is my validation if statement not working?
Option Strict On
Public Class frmPickUpSticks
Dim playersTurn As Boolean = False
Dim remainingSticks As Integer 'count sticks
Dim losses As Integer = 0 'count player losses
Private Sub btnNewGame_Click(sender As Object, e As EventArgs) Handles btnNewGame.Click
lblOutput.Text = ""
remainingSticks = CInt(InputBox("How many matchsticks would you like (5 - 25)?", "Pick Number of Matches!"))
'Validate input
If IsNumeric(remainingSticks) Then
If (remainingSticks >= 5) And (remainingSticks <= 25) Then
DisplayMatches()
If (remainingSticks Mod 4 = 1) Then
MessageBox.Show("You go first!")
playersTurn = True
turns()
Else
MessageBox.Show("I go first.")
turns()
End If
Else
MessageBox.Show("Please enter a number between 5 and 25.")
End If
Else
MessageBox.Show("Input must be numeric.", "Input Error")
End If

You can't automatically take what your user types in an InputBox and pass this input to any function or method that expects a number for input. The InputBox method has been designed to return a string and this string need to be converted but you need to use methods that know how to parse a string. Otherwise methods that are not designed to handle nonnumeric values (CInt) will cause exceptions.
Instead you should try some kind of parsing and the NET Library offers many tools to use. In your case the correct one is Int32.TryParse
Dim remainingSticks As Integer
Dim userInput = InputBox("How many matchsticks .....")
If Int32.TryParse(userInput, remainingSticks) Then
.... ok your remainingStick contains the converted value
Else
MessageBox.Show("You should type a valid integer number between 5-25")
The Int32.TryParse will look at your input string and try to convert to a valid integer value. If it succeed then the second parameter contains the converted integer and returns True, if it fails it return false and your second parameter will have the default value of zero.
Of course after a succesfull conversion to an integer your don't need anymore a check with IsNumeric

You should use a string variable in the inputbox
dim st as string
st = InputBox("How many matchsticks would you like (5 - 25)?", "Pick Number of Matches!"))
remainingSticks = val(st)
. . .

Related

Visual Basic Validation

I'm new to Visual Basics, and I tried implementing input validation for the Mark input in my application where the input can't be empty or < 0 or > 100 or isnumeric(Mark) = False or else an error message would be displayed. However, I tried leaving the input as empty or entering alphabetical values but it returned me a run time error instead. Could anyone assist me in some explanations as well as how to overcome this? Apologies if this is a dumb question.
Note: The data type for the variable "Mark" is set as single. Thank you for your time!
Sub InputMark()
4: For I = 0 To 3
Console.WriteLine("Please enter test marks for test " & I + 1)
Mark(I) = Console.ReadLine
While Mark(I) = Nothing Or Mark(I) < 0 Or Mark(I) > 100 Or IsNumeric(Mark(I)) = False
Console.WriteLine("Please enter valid test marks.")
GoTo 4
End While
Next
End Sub
A couple of things:
Turn option strict on: https://github.com/dday9/vb.net-tutorial/blob/master/Section%201/Chapter%201.md
Using Single.TryParse on the result of ReadLine.
ABANDON GOTO'! In this case a simple Do loop will suffice.
Take a look at this example:
Option Strict On
Option Explicit On
Imports System
Public Module Module1
Public Sub Main()
' Declare an array of Single values
Dim marks(3) As Single
' Loop from 0 to n (in this case 3)
For counter As Integer = 0 To marks.Length - 1
' Declare a variable to check if the conversion was succesful
Dim success As Boolean = False
' Start a Do/Until loop
Do
' Propt the user to enter something
Console.WriteLine("Please enter test marks for test " & counter + 1)
' Check if the conversion from ReadLine to a Single is valid
success = Single.TryParse(Console.ReadLine(), marks(counter)) AndAlso marks(counter) >= 0 AndAlso marks(counter) <= 100
' If not then scold the user
If (Not success) Then
Console.WriteLine("Please enter valid test marks.")
End If
Loop Until success
Next
' Debug - just print out the values of marks
For Each mark As Single In marks
Console.WriteLine(mark)
Next
End Sub
End Module
Live Demo: https://dotnetfiddle.net/guoAzP
The data type for the variable "Mark" is set as single.
One of the nice things about strongly-typed platforms like .Net is most of your validation rules are already enforced. You will not be able to assign a non-number value to Mark, and as a value-type it can't even be null (VB.Net's Nothing will evaluate to 0 for a Single type).
So what we need to do instead is back up, and find where the data is received from the user, before it's assigned to the Mark variable. Somewhere you'll have code like Mark(I) = Console.Readline(), or similar. That's where your testing needs to be. Check the string value received from the user before assigning it to the Single value. And when you come to that point, the best way to accomplish this testing is with the Single.TryParse() function. Then, you can verify the parsed Single value is within the correct range.
Okay, with more complete code we can start to give you some real improvements.
One suggestion is to think in terms of returning a value. It's better design to let this function return the array, rather than communicating through a global variable. I mention that here because it will also mean changing how you call this function.
That out of the way:
Function InputMarks() As Single()
Dim result(3) As Single
For I As Integer = 0 To 3
Dim ErrorMsg As String = ""
Do
Console.Write($"{ErrorMsg}Please enter test marks for test {I+1}: ")
Dim input As String = Console.ReadLine()
ErrorMsg = $"{vbCrLf}Please enter valid test marks.{vbCrLf}{vbCrLf}"
While Not Single.TryParse(input, result(I)) OrElse result(I) < 0 OrElse result(I) > 100
Next
Return result
End Function
No GOTO required or wanted.
If you haven't seen them yet, the $"strings" are using string interpolation and the OrElse operator instead of Or is to get short-circuiting behavior. Modern VB.Net should use OrElse and AndAlso instead of Or and And pretty much all the time.
In this case, using OrElse means we're sure Single.TryParse() completed successfully and we will have a valid number before attempting to verify the range. If the parse operation failed, the range tests won't even be attempted.
I'm going to break this into smaller functions.
First, here is a function to process a string, which returns either a number if it satisfies your conditions, or Single.NaN.
Private Function validateMarkOrNaN(input As String) As Single
Return If(Single.TryParse(input, validateMarkOrNaN) AndAlso 0 <= validateMarkOrNaN AndAlso validateMarkOrNaN <= 100, validateMarkOrNaN, Single.NaN)
End Function
Here is a second function which calls the first when it needs to process the user input. This function handles the flow of a single input. It can be called any number of times, requiring that you pass in the index only so it can be typed in the console.
Private Function getMarkInput(index As Integer) As Single
Dim result As Single
Do
Console.WriteLine($"Please enter test marks for test {index + 1}")
result = validateMarkOrNaN(Console.ReadLine())
If Single.IsNaN(result) Then Console.WriteLine("Please enter valid test marks.")
Loop While Single.IsNaN(result)
Return result
End Function
Your main program dictates the overall flow. Here you can declare how many marks will be read, and loop over your array, getting each input into each element.
Sub Main()
Dim numberOfMarks = 4
Dim Mark(numberOfMarks - 1) As Single
For i = 0 To numberOfMarks - 1
Mark(i) = getMarkInput(i)
Next
End Sub
Similar to your code, this will loop indefinitely as long as the user doesn't enter a valid float.

How to check if input comes null/empty

I need to check if an input comes null/empty.
If input <= 100 And input > 0 And Not input = "" Then
subjectsInt.Add(subjects(i), input)
check = True
End If
I know in that code I am checking it as String but I already did Not input = Nothing and Not input = 0 and I am getting an error:
Run-time exception (line -1): Conversion from string "" to type 'Integer' is not valid.
Stack Trace:
[System.FormatException: Input string was not in a correct format.]
[System.InvalidCastException: Conversion from string "" to type 'Integer' is not valid.]
Any suggestions?
EDIT
Here is the code in case you want to take a look: https://dotnetfiddle.net/0QvoAo
Well, I have looked at your code and there are many problems, all caused by the freely approach to Data Types that VB.NET allows when you have Option Strict set to Off. In this context VB.NET allows you to assign the return value of Console.ReadLine to an Integer trying to help you adding an implicit conversion of the input. Of course if the user types ABCD this implicit conversion has no other way to inform you than triggering an Exception. So I really, really recommend you to use Option Strict set to On (MSDN Option Strict)
Now with Option Strict On your code has a lot of errors, I have rewritten your code and then explain in comments the changes.
Option Strict On
Imports System
Imports System.Collections.Generic
Public Module Module1
Public Sub Main()
Console.WriteLine("Hello World")
' I use a List of strings instead of an untyped array list
Dim subjects As New List(Of String)() From
{
"Math",
"English",
"German"
}
Dim subjectsInt As New System.Collections.Generic.Dictionary(Of String, Integer)
Dim i, input As Integer
Dim check As Boolean
For i = 0 To subjects.Count - 1
check = False
Do
Console.WriteLine(subjects(i) & ": ")
' The input is passed to a string
Dim temp = Console.ReadLine()
' Check if this string is really a number
If Int32.TryParse(temp, input) Then
If input <= 100 And input > 0 Then
subjectsInt.Add(subjects(i), input)
check = True
End If
End If
Loop While check = False
Next
For i = 0 To subjects.Count - 1
Console.WriteLine(subjects(i) & ": " & subjectsInt(subjects(i)))
Next
Console.ReadLine()
End Sub
End Module
The error is telling you that the input is an integer, you can't compare an integer with a string which is precisely what's happening.
Even if the string contains only digit, the type system doesn't convert it inferring from the operator.
In your example, input = "" input can never be "". You should change the type of input to a string and then check before converting it to an integer.
This would be a solution:
Dim integerInput As Integer
Dim stringInput = Console.ReadLine()
If Integer.TryParse(stringInput, integerInput) AndAlso integerInput <= 100 And integerInput > 0 Then
To use < > = with an integer you need to convert the string:
If Integer.Parse(input) <= 100 And Integer.Parse(input) > 0 And Not input = "" Then
Integer.Parse will do the job.
Obviously, if your input is a string and it's not guaranteed that it is going to contain just digit you need either to do some check before or use Integer.TryParse.
The closest you can get to a null check with a value type is EqualityComparer(Of Integer).Default.Equals(id, Nothing); unless you start using Nullable, but it would be too late as the code will fail on the input = Console.ReadLine() when you input a non-digit string.

How can I make this VB program recursive?

I need to use recursion in this program for a school project. The program checks if the number inputted is a real number (in this case defined by a number with a decimal with the characters 0-9 (e.g. 56.7). How would I make the function recursive?
Thanks :-)
Module Real_Numbers
Sub Main()
Dim number As String
Dim check As Boolean
Console.WriteLine("Enter a number to check if it is a real number:")
number = Console.ReadLine()
check = CheckNumber(number)
If check = True Then
Console.WriteLine("The number is a real number")
Else
Console.WriteLine("The number is not a real number")
End If
Console.ReadLine()
End Sub
Function CheckNumber(ByVal number As String) As Boolean
Dim current As Char
For i As Integer = 0 To number.Length - 1
current = number.Substring(i, 1)
If current = "." Then
' Do nothing
Else
If IsNumeric(current) Then
' Do nothing
Else
Return False
End If
End If
Next
Return True
End Function
End Module
Given that this is a homework assignment, I'm not going to write the code out for you. But I will say this -- there are a couple of ways to set this up. One straightforward way would be to pass the string (of the number) to CheckNumber and then check the first character -- if it's numeric, call CheckNumber again with the remainder of the string (everything minus what you just checked). If it's not numeric, return false. You'll need a special case to handle the very last character -- if it's numeric, then return true. If you propagate the boolean response properly, your recursion should unwind itself at the end with the right answer.
Good luck!
You should call the CheckNumber function from within itself, that is a recursion. Learn more about recursion here.

Text from Form as Integer in Module Function

Working with Visual Basic in Visual Studio 2013. I need to take input from a textbox on form1 and use it as an integer in functions of a module. When I call one of the functions on form1 after a click event, I get an error for invalid arguments on the public function integers. How can I get the text passed to the module and then treated as an integer?
This is what I have on form1. This worked okay on the last project, which required the calculations to be performed and displayed only on form1. This project requires calculations to be performed in a module I created, and then displayed in labels on form2. (I'm still very new to this).
'Define inputs as public variables
Public intNumber1 As Integer
Public intNumber2 As Integer
'Create Function to validate inputs as integers
Public Function ValidateInputFields() As Boolean
'Try to convert each input to integer. If not, return error message, clear input, and return focus
If Not Integer.TryParse(txtNumber1.Text, intNumber1) Then
MessageBox.Show("Please enter only whole numbers.")
txtNumber1.Clear()
txtNumber1.Focus()
Return False
End If
If Not Integer.TryParse(txtNumber2.Text, intNumber2) Then
MessageBox.Show("Please enter only whole numbers.")
txtNumber2.Clear()
txtNumber2.Focus()
Return False
End If
Return True
End Function
This is the error message I get:
Error 1 Argument not specified for parameter 'intNumber1' of 'Public
Function AddInt() As Integer'.
This is the function I've written in the module:
'Create function to pass the values and add
Public Function AddInt(ByVal intNumber1 As Integer, intNumber2 As
Integer) As Integer
'Define intSum
Dim intSum As Integer
'AddInt adds numbers 1 and 2
intSum = intNumber1 + intNumber2
'Return the result
Return intSum
End Function
Text from a TextBox will be of type String and you need to convert it to an integer after checking whether its content can be safely interpreted as a number.
Something like this :
Dim num As Integer
If Not Integer.TryParse(TextBox1.Text, num) Then
'... it's not an integer, so don't try to use it
Else
'... call method using "num" as your integer parameter
End If
This is how I finally got my function calls to work correctly:
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles
btnAdd.Click
'create instance for results form and define sum variable
Dim frmResults As New Results
Dim intSum As Integer
'If inputs are valid, display the integer sum on results form
If ValidateInputFields() Then
'Call Add Integer function from module
intSum = AddInt(intNumber1, intNumber2)
'Send answers to Results form and display
frmResults.lblNumber1Result.Text = intNumber1.ToString()
frmResults.lblOperatorResult.Text = "plus"
frmResults.lblNumber2Result.Text = intNumber2.ToString()
frmResults.lblEqualsResult.Text = intSum.ToString()
frmResults.ShowDialog()
End If
Everything worked once I added the (intNumber1, intNumber2) right after the function call of AddInt.
The ValidateInputFields() function is the TryParse method that #mcrimes was helping me with earlier. This did handle any non-numeric inputs or blanks left as it did in my previous project.
Thanks again #Jonathan and #mcrimes for all of your help.

Validating a string

I am trying to write a program where the user inputs a telephone number and a message is displayed if it contains a letter.
Module Module1
Sub Main()
Dim TelNumber As String
Dim Character As String
TelNumber = Console.ReadLine()
For Number = 0 To TelNumber.Length - 1
Character = TelNumber.Substring(Number)
If Integer.TryParse(Character, 0) <> 0 Then
Console.WriteLine("It cannot contain a letter")
Exit For
End If
Next
Console.ReadLine()
End Sub
End Module
However with this code even it only works properly if the string contains less than 11 charcaters, after that even if it does not contain any letters it still displays the message. How do I fix this? Also I don't understand what the second parameter of the Integer.TryParse function represents?
TelNumber.Substring(Number) does not just the Numberth character of the string.
It returns the string with Number characters stripped off its beginning.
Thus, in the first step of the loop, TelNumber.Substring(0) returns the whole string.
Then, Integer.TryParse() fails with integer overflow with long string.
Hint: simple string validation is a task for regular expressions.
With regular expressions it will also be very easy to extend tel.number format to something like +4915771828000 or 12-34-56.
Imports System.Text.RegularExpressions
Module Module1
Sub Main()
Dim telNumber As String = Console.ReadLine()
Dim telValidationRegex As Regex = New Regex("^\d+$")
If NOT telValidationRegex.Match(telNumber).Success Then
Console.WriteLine("Wrong telephone number format")
End If
Console.ReadLine()
End Sub
End Module
I don't have a compiler handy, but I can tell you what I think this should look like. First let's look at Int32.TryParse's documentation
result
Type: System.Int32
When this method returns, contains the 32-bit signed integer value equivalent of the number contained in s, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the s parameter is null or String.Empty, is not of the correct format, or represents a number less than MinValue or greater than MaxValue. This parameter is passed uninitialized.
So, the second parameter is supposed to be an integer variable that contains the result of trying to parse the string.
Then, let's look at substring
startIndex
Type: System.Int32
The zero-based starting character position of a substring in this instance.
So what you're doing isn't looking at each character and trying to convert it to an integer. It's looking at the whole number, and trying to convert that to an integer. Then the whole number, except for the first character, and trying to convert that to a number. And so on.
I suspect you'll find that Int32 can only store numbers about 10 digits long (2^32, or 4294967296)
What you want to do is look at each character, something like this.
Module Module1
//Warning, did not try to compile
Sub Main()
Dim TelNumber As String
Dim Character As String
TelNumber = Console.ReadLine()
For Number = 0 To TelNumber.Length - 1
Character = TelNumbert(Number)
Dim OutputNumber as Integer
If Not Integer.TryParse(Character, OutputNumber) Then
Console.WriteLine("It cannot contain a letter")
Exit For
End If
Next
Console.ReadLine()
End Sub
End Module