I am taking a user input a text box and then Validating it as follows:
Val(txt_score1.text)
Then I have to compare it with blank entry in that text box. Like this:
If (Val(txt_score1.Text) = "") Then....
I am receiving a conversion error. Because "" is String whereas Val is returning Integer.
How to overcome this??
You can use Integer.TryParse to determine if the value is a proper integer:
Dim x As Integer
If Integer.TryParse(TextBox1.Text, x) Then
MessageBox.Show(x)
Else
MessageBox.Show("'" + TextBox1.Text + "' is not a valid number")
End If
If you need to just check for a blank string, you can use String.IsNullOrEmpty on the text itself:
If String.IsNullOrEmpty(TextBox1.Text) Then
MessageBox.Show("String is empty")
End If
Val is a legacy function leftover from VB6 days and has some weird behavior if you don't know about it. I avoid it for this reason. For example, take the following cases and the output they generate:
MessageBox.Show(Val("")) '0
MessageBox.Show(Val("5")) '5
MessageBox.Show(Val("5e3")) '5000
MessageBox.Show(Val("5xyz")) '5
Related
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.
I'm trying to remove a specific string out of a label. The label is suppose to print an error message while the input is invalid, as soon as the user changes the textbox input to a number, the error message should disappear for that specific input.
Here is what I tried so far but the "-=" did not work as I expected.
Sub errorOutput(toggleError As Boolean, courseNumber As Integer)
Dim err(6) As String
err(courseNumber) = "please ensure that what you input in Course " + courseNumber.ToString + " is a number between 0 and 100"
If toggleError = True Then
lblError.Text += err(courseNumber)
Else
lblError.Text -= err(courseNumber)
End If
End Sub
When applied to two Strings, the + operator does concatenation. It appends the string on the right-hand side of the operator to the string on the left-hand side of the operator.
(Technically, it creates a new String object that represents the concatenation of the left-hand and right-hand strings, since strings are immutable in .NET, but that's a technical detail you probably don't care about.)
The += operator is referred to as a compound assignment operator. It merges together an operator that modifies the value (in this case, the +) with the assignment operator (=), so that you can get both at once.
Okay, so you know how this works already. You're using +=, and you can append two strings, and everything is good.
So, by way of analogy, you try -=. Wait…what should happen? What does it mean to subtract two strings? Subtracting two numbers, well, that makes perfect sense. But how do you subtract a string?
It's not obvious what operation would be performed, so the subtraction operator doesn't do anything for String objects. It hasn't been defined, because its semantics are not clear. Only concatenation is defined, using the + operator (and also the & operator).
If you need to "remove" a portion of a string, you will have to write a bit more code, using one or more of the methods provided by the String class to spell out exactly what operation you want to happen. For example, you could use the String.Replace method to replace all occurrences of one string with another string (or even an empty string):
Dim message As String = "My favorite color is yellow."
message = message.Replace("yellow", "blue")
But in this case, you probably just want to clear the Label control first, and then only add in the error messages. This is essentially what Ry- suggested in a comment; e.g.:
Sub errorOutput(toggleError As Boolean, courseNumber As Integer)
lblError.Text = String.Empty
Dim err(6) As String
err(courseNumber) = "please ensure that what you input in Course " + courseNumber.ToString + " is a number between 0 and 100"
If toggleError = True Then
lblError.Text += err(courseNumber)
End If
End Sub
There is some built in things that can be used for validating user input. This is just some info I saved on the subject. Easy to use.
Validation
ErrorProvider
Private err As New ErrorProvider()
Or add to your form from Toolbox -> Components in Design View
.SetError(Control, “Error Message”)
Control.Validating
Private Sub TextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If TextBox1.Text = "" Then
e.Cancel = True
err.SetError(TextBox1, "This text box cannot be blank.")
Else
err.Clear()
End If
End Sub
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.
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)
. . .
i have managed to find a string between 2 specified strings,
the only issue now is that it will only find one and then stop.
how am i possible to make it grab all the strings in a textbox?
the textbox is multiline and i have put a litle config in it.
now i want that the listbox will add all the strings that are between my 2 specified strings.
textbox3.text containts "<"
and textbox 4.text contains ">"
Public Function GetClosedText(ByVal source As String, ByVal opener As String, ByVal closer As String) As String
Dim intStart As Integer = InStr(source, opener)
If intStart > 0 Then
Dim intStop As Integer = InStr(intStart + Len(opener), source, closer)
If intStop > 0 Then
Try
Dim value As String = source.Substring(intStart + Len(opener) - 1, intStop - intStart - Len(opener))
Return value
Catch ex As Exception
Return ""
End Try
End If
End If
Return ""
End Function
usage:
ListBox1.Items.Add(GetClosedText(TextBox1.Text, TextBox3.Text, TextBox4.Text))
The easiest way (least lines of code) to do this would be to use a regular expression. For instance, to find all of that strings enclosed in pointy brackets, you could use this regular expression:
\<(?<value>.*?)\>
Here's what that all means:
\< - Find a string which starts with a < character. Since < has a special meaning in RegEx, it must be escaped (i.e. preceded with a backslash)
(?<value>xxx) - This creates a named group so that we can later access this portion of the matched string by the name "value". Everything contained in the name group (i.e. where xxx is), is considered part of that group.
.*? - This means find any number of any characters, up to, but not including whatever comes next. The . is a wildcard which means any character. The * means any number of times. The ? makes it non-greedy so it stops matching as soon as if finds whatever comes next (the closing >).
\> - Specifies that matching strings must end with a > character. Since > has a special meaning in RegEx, it must also be escaped.
You could use that RegEx expression to find all the matches, like this:
Dim items As New List(Of String)()
For Each i As Match In Regex.Matches(source, "\<(?<value>.*?)\>")
items.Add(i.Groups("value").Value)
Next
The trick to making it work in your scenario is that you need to dynamically specify the opening and closing characters. You can do that by concatenating them to the RegEx, like this:
Regex.Matches(source, opener & "(?<value>.*?)" & closer)
But the problem is, that will only work if source and closer are not special RegEx characters. In your example, they are < and >, which are special characters, so they need to be escaped. The safe way to do that is to use the Regex.Escape method, which only escapes the string if it needs to be:
Private Function GetClosedText(source As String, opener As String, closer As String) As String()
Dim items As New List(Of String)()
For Each i As Match In Regex.Matches(source, Regex.Escape(opener) & "(?<value>.*?)" & Regex.Escape(closer))
items.Add(i.Groups("value").Value)
Next
Return items.ToArray()
End Function
Notice that in the above example, rather than finding a single item and returning it, I changed the GetClosedText function to return an array of strings. So now, you can call it like this:
ListBox1.Items.AddRange(GetClosedText(TextBox1.Text, TextBox3.Text, TextBox4.Text))
I ssume you want to loop all openers and closers:
' always use meaningful variable/control names instead of
If TextBox3.Lines.Length <> TextBox4.Lines.Length Then
MessageBox.Show("Please provide the same count of openers and closers!")
Return
End If
Dim longText = TextBox1.Text
For i As Int32 = 0 To TextBox3.Lines.Length - 1
Dim opener = TextBox3.Lines(i)
Dim closer = TextBox4.Lines(i)
listBox1.Items.Add(GetClosedText(longText, opener , closer))
Next
However, you should use .NET methods as shown here:
Public Function GetClosedText(ByVal source As String, ByVal opener As String, ByVal closer As String) As String
Dim indexOfOpener = source.IndexOf(opener)
Dim result As String = ""
If indexOfOpener >= 0 Then ' default is -1 and indices start with 0
indexOfOpener += opener.Length ' now look behind the opener
Dim indexOfCloser = source.IndexOf(closer, indexOfOpener)
If indexOfCloser >= 0 Then
result = source.Substring(indexOfOpener, indexOfCloser - indexOfOpener)
Else
result = source.Substring(indexOfOpener) ' takes the rest behind the opener
End If
End If
Return result
End Function