Using Array with Instr - vb6

I'm creating a chatbot using VB6 and the whole basis of this chatbot is that whenever a user writes a 'trigger word', such as "good", "amazing", etc., the chatbot will reply "that's great".
However, whenever I don't write the trigger word, only the msgbox which says "Word is in this" appears, and I don't know what I'm doing wrong. I've tried messing around with the >, <, = signs to no avail. Any help will be appreciated.
Private Sub conversation()
Dim imput As String 'where user types to chatbot
Dim arrWords As Variant, aWord As Variant
arrWords = Array("good", "great", "bad")
Dim wordFound As Boolean
wordFound = False
For Each aWord In arrWords
If InStr(imput, aWord) = 0 Then
wordFound = True And MsgBox "Word is in this"
ElseIf InStr(imput, aWord) > 0 Then
wordFound = False And MsgBox "Word is not in this"
End If
Next
End Sub

Other answers have already explained that your logic is backwards, which it is.
But also, I don't know whether you have accurately copied your code or not, but when I copy it into VB, I get — as I expected — syntax errors on the lines with MsgBox in them. I can "fix" this by adding parentheses to the MsgBox arguments, so: wordFound = True And MsgBox("Word is in this"), etc. But that isn't good code, for reasons I will explain, and I have a few other suggestions.
Consider these changes to your code:
Private Sub conversation(theInput As String)
Dim arrWords As String, aWord As String
arrWords = Array("good", "great", "bad")
Dim wordFound As Boolean
wordFound = False
For Each aWord In arrWords
If InStr(theInput, aWord) = 0 Then
wordFound = False
MsgBox """" & aWord & """ is in this"
Else
wordFound = True
MsgBox """" & aWord & """ is not in this"
End If
Next
End Sub
Private Sub SendButton_Click()
conversation(myChatTextBox.Text)
End Sub
Ok. Here are some points.
Don't use Variant unless you have a compelling reason to do so. It's the least efficient way to store information, because it has to allocate extra memory to tell internally what type of variable it is, and it also has to allocate enough memory to contain the largest possible type it could be. (A String variable has 10 bytes plus one per character in the string, while a Variant type allocated as a string has 22 bytes plus one per character in the string.)
I changed imput to theInput. input is a reserved word in VB, so you can't use it, but it's clearer to other people if you don't misspell the word. Better to find some prefix to put on it.
As others have said, when InStr returns zero, that means that the string in argument 2 isn't in the string in argument 1. So, it means that the "word isn't in this," not that it is. (That's the answer to the trouble you're having; the rest of this is just to improve your code overall.)
wordFound = True And MsgBox("Word is in this") only works by coincidence. MsgBox returns a value of 1 when it runs successfully without arguments. (Who knew? I had to try it out for myself. Probably because it can be set up to return a number of different values for different types of ms) So your logic is wordFound = True And 1. And is a logical comparison operator: True And 1 evaluates to True, while False And 1 evaluates to False. So, you get what you want, but pretty much by accident. Put the two code bits on two different lines. You don't need to logically compare them; in fact it doesn't make sense to do so. (If you want to actually put two lines of code on the same line, put a colon between them: wordFound = True : MsgBox "etc", but this isn't generally considered good practice as the code is less readable. I have the feeling you thought you were using And to do this, and as you can see it does something quite different.)
I changed your message to include the word you're looking for in quotes, for example "good" is in this. To get a literal quotation mark in a string, use two of them: "". (You have to put the two quotes in quotes themselves, since they are a quoted string; that's why there are four of them at the beginning and three later on.)
You don't need an ElseIf here, because if your If condition is false, your ElseIf condition is true. You only need ElseIf if you are evaluating more than two possible conditions.
I've set up the basic idea of how to send the chatbox user's input to your conversation subroutine. When the user clicks a Send button, you send the contents of the text box to conversation as the input argument. If you set it up as a local variable, you have to write some sort of code to grab the user's input. This is the cleaner way to do the job.
All that said, you can further simplify your For Each loop like this:
For Each aWord In arrWords
wordFound = InStr(input, aWord) > 0
MsgBox """" & aWord & """ is" & IIf(wordFound, "", " not") & " in this"
Next
Explanations:
InStr(input, aWord) <> 0 is either true or false. You assign whichever it is to wordFound. This is a more concise way of doing your If...Else. (A simpler example of the idea: x = 1 = 1 will set x equal to True, while x = 1 = 0 will set x equal to false. You can use parentheses to make it easier to understand: x = (1 = 1), etc.)
IIf ("instant if") takes the form of IIf(condition, valueIfConditionIsTrue, valueIfConditionIsFalse).
Since wordFound is a boolean value, saying wordFound is the same as saying wordFound = True, so you can omit the = True (you can also say Not wordFound to mean the same thing as wordFound = False).
EDIT: If you want your chatbot to reply "that's great" when any one of the words is encountered, change conversation() from a Sub to a Function and return true or false. Like this:
Private Function conversation(theInput As String) As Boolean
Dim arrWords As String, aWord As String
arrWords = Array("good", "great", "bad")
'Get rid of your wordFound variable — you don't need it any more
conversation = False
For Each aWord In arrWords
If InStr(theInput, aWord) > 0 Then
conversation = True
End If
Next
End Function
Private Sub SendButton_Click()
If conversation(myChatTextBox.Text)
MsgBox "That's great!"
Else
MsgBox "That's not so great."
End If
End Sub

Jim Mack's answer is correct; I would make the comparison more robust by adding (before the for loop):
imput = UCase(imput)
and making the array of test words all uppercase. Fundamentally, there was just a logic problem with the original code (probably due to a misunderstanding of the language).
The ElseIf clause is somewhat worrisome, too; in general, it's wise to include an explicit else clause to handle any test that falls through all the previous conditions.

I didn't try to run your code -- it doesn't even look like it would run -- but at least one error jumps out.
If InStr(imput, aWord) = 0 Then
wordFound = True And MsgBox "Word is in this"'
...doesn't do what you seem to think it does. You're saying that wordFound will be true when the word is NOT in the input (InStr = 0), but only when the MsgBox returns a non-zero result
What you want is something like:
If InStr(imput, aWord) > 0 Then ' >0 means word was found
wordFound = True
MsgBox "Word is in this"'
The parallel is true for the 'not found' condition.

Related

Using Empty vs "" to define or test a variable in VBscript

When declaring a variable to blank (before a loop for example), it is sometimes done as "" or Empty. Also, when checking the value, it is sometimes used with "(Not IsEmpty(variable))" and "variable <> Empty". Is it better to use one vs another and can it cause any issues using it the wrong way?
Ex1:
fileNameDate = Empty
fileNameDate = ""
Ex2:
If (Not IsEmpty(fileNameDate)) Then
If fileNameDate <> Empty Then
If fileNameDate <> "" Then
Thanks!
------------Update-------------
Note that my question is not asking for the difference between Null, Empty, and Nothing. I'm simply concentrating on the "Empty" part and whether it's the same things as writing "". For the most part, I have received similar results when interchanging the two, but I dont know if it's just the examples I used. For example, the following confused me some.
My code:
Dim x, y, z
'Option1 (Do not set x to anything)
'Option2
'x = Empty
'Option3
'x = ""
If x = "" Then
'Action1
End if
If x = Empty Then
'Action2
End if
If IsEmpty(x) Then
'Action3
End if
If I go with Option1 and just not define "x", all three actions will occur.
If I go with Option2 and set x = Empty, all three actions will also occur
But if I go with Option3 and set x = "", only Action1 and Action2 occurs.
Why?
Some considerations:
fileNameDate = Empty ' The same as just declaring Dim fileNameDate
IsEmpty(fileNameDate) ' = True
Is not the same as:
fileNameDate = ""
IsEmpty(fileNameDate) ' = False
I think the function IsEmpty()is misnamed, because it checks if the variable has been initialized, not if it's actually empty.

Validating a string vbscript

I am trying to search a string to make sure it does not contain a numbers followed by a comma followed by numbers and containing a dot and 2 decimal places, e.g. 32,000.00. At the moment, I have it searching to see if the string contains a dot only. How do I go about searching the criteria above?
Root_RefundAmount = Root_TaxDataSummary.SlvObject("RefundAmount").GetROProperty("text")
refund = InStr(1,Root_RefundAmount,".")
If refund > 0 Then
pass
Else
fail
End If
I believe a simple Regular Expression Test will provide you what you want:
Dim objRegEx, strValue
'Create Regular Expression object to parse the file
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.Global = True
objRegEx.MultiLine = True
objRegEx.Pattern = "\d+\,\d+\.\d{2}"
strValue = "e.g. 32,000.00."
MsgBox objRegEx.test(strValue) '<- True
strValue = "e.g. 3200000."
MsgBox objRegEx.test(strValue) '<- False
The above will result in a Boolean response (True/False).
Adapting it to your routine...
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True
objRegEx.Global = True
objRegEx.MultiLine = True
objRegEx.Pattern = "\d+\,\d+\.\d{2}"
Root_RefundAmount = Root_TaxDataSummary.SlvObject("RefundAmount").GetROProperty("text")
If Not objRegEx.test(Root_RefundAmount) Then
'Pattern NOT Found
Else
'Pattern IS Found
End If
To explain the Pattern:
"\d+\,\d+\.\d{2}"
\d = Any Number 0 through 9...
"A numbers followed by a comma followed by numbers and containing a dot and 2 decimal places"{Assume you mean 2 Numbers}... Depending on your input the above is much easier to look at but it will also match numbers such as 12345,12345.99 which may or may not be desirable.
If you need to be precise then you may want to consider the below Regex:
"\d{1,3}(?:\,\d{3})*\.\d{2}"
This will match numbers from 0.00 to 999,999,999.99 etc in typical numeric format...
If you want to specifically look for numbers above 1,000 (With a Comma) Then change the Star '*' to a Plus '+' to mean One or More are Required...
"\d{1,3}(?:\,\d{3})+\.\d{2}"
Or for an even more specific Regex:
(?:[1-9]|[1-9][0-9]|[1-9][0-9]{2})(?:\,[0-9]{3})+\.[0-9]{2}
'OR
(?:[1-9]|[1-9]\d|[1-9]\d{2})(?:\,\d{3})+\.\d{2}
For a little more info on Regular Expressions and the '\d' Character Class, Please take a look at the below links:
http://www.regular-expressions.info/shorthand.html
https://msdn.microsoft.com/en-us/library/20bw873z(v=vs.110).aspx#Anchor_9

Is there a direct funtion to count numbers inside a string?

I have a string that contains digits inside. For example, "adf20j83n,m3jh2k9". Is there a direct way to count the number of digits inside the string. As in my example, it should give me "7" as an output.
Also, I have tried RegExp but it's not working in VBScript in QTP.
Btw, I'm not looking for loops and stuff like that. Just a direct way, or a suggestion to make this RegExp work in QTP.
You'll probably need to create the COM object via its ProgId:
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "\d"
re.Global = True
MsgBox "Digits: " & re.Execute("adf20j83n,m3jh2k9").Count
Output:
Digits: 7
I know it's techically what you said but it's a good way nonetheless
Try using this function:
Public Sub CountNumeric(ByVal input As String)
Dim numericCount As Integer = 0
For Each c As Char In input
If Char.IsDigit(c) Then numericCount += 1
Next
MessageBox.Show(String.Format("Number of numerics : {0}", numericCount)
End Sub
EDIT:
You could also try this:
Dim charColl As MatchCollection = Regex.Matches(input , "^\d+$")
Console.WriteLine(charColl.Count.ToString())

Classic ASP InStr() Evaluates True on Empty Comparison String

I ran into an issue with the Classic ASP VbScript InStr() function. As shown below, the second call to InStr() returns 1 when searching for an empty string in a non empty string. I'm curious why this is happening.
' InStr Test
Dim someText : someText = "So say we all"
Dim emptyString : emptyString = ""
'' I expect this to be true
If inStr(1,someText,"so",1) > 0 Then
Response.write ( "I found ""so""<br />" )
End If
'' I expect this to be false
If inStr(1, someText, emptyString, 1) > 0 Then
Response.Write( "I found an empty string<br />" )
End If
EDIT:
Some additional clarification: The reason for the question came up when debugging legacy code and running into a situation like this:
Function Go(value)
If InStr(1, "Option1|Option2|Option3", value, 1) > 0 Then
' Do some stuff
End If
End Function
In some cases function Go() can get called with an empty string. The original developer's intent was not to check whether value was empty, but rather, whether or not value was equal to one of the piped delimited values (Option1,Option2, etc.).
Thinking about this further it makes sense that every string is created from an empty string, and I can understand why a programming language would assume a string with all characters removed still contains the empty string.
What doesn't make sense to me is why programming languages are implementing this. Consider these 2 statements:
InStr("so say we all", "s") '' evaluates to 1
InStr("so say we all", "") '' evaluates to 1
The InStr() function will return the position of the first occurrence of one string within another. In both of the above cases, the result is 1. However, position 1 always contains the character "s", not an empty string. Furthermore, using another string function like Len() or LenB() on an empty string alone will result in 0, indicating a character length of 0.
It seems that there is some inconsistency here. The empty string contained in all strings is not actually a character, but the InStr() function is treating it as one when other string functions are not. I find this to be un-intuitive and un-necessary.
The Empty String is the Identity Element for Strings:
The identity element I (also denoted E, e, or 1) of a group or related
mathematical structure S is the unique element such that Ia=aI=a for
every element a in S. The symbol "E" derives from the German word for
unity, "Einheit." An identity element is also called a unit element.
If you add 0 to a number n the result is n; if you add/concatenate "" to a string s the result is s:
>> WScript.Echo CStr(1 = 1 + 0)
>> WScript.Echo CStr("a" = "a" & "")
>>
True
True
So every String and SubString contains at least one "":
>> s = "abc"
>> For p = 1 To Len(s)
>> WScript.Echo InStr(p, s, "")
>> Next
>>
1
2
3
and Instr() reports that faithfully. The docs even state:
InStr([start, ]string1, string2[, compare])
...
The InStr function returns the following values:
...
string2 is zero-length start
WRT your
However, position 1 always contains the character "s", not an empty
string.
==>
Position 1 always contains the character "s", and therefore an empty
string too.
I'm puzzled why you think this behavior is incorrect. To the extent that asking Does 'abc' contain ''? even makes sense, the answer has to be "yes": All strings contain the empty string as a trivial case. So the answer to your "why is this happening" question is because it's the only sane thing to do.
It is s correct imho. At least it is what I expect that empty string is part of any other string. But maybe this is a philosophical question. ASP does it so, so live with it. Practically speaking, if you need a different behavior write your own Method, InStrNotEmpty or something, which returns false on empty search string.

"Expected End of Statement" in Loop

I am getting an "expected end of statement" error at line 26, the third to last line. If you look at the code, it is a simple game that has one person enter a word, the script replaces all consonants with underscores, and the second player has to guess the word. Line 26 is, I think, the only thing wrong with this program.
phrase=inputbox("Player 1: Enter a phrase","Guessing Game")
phrase=answer
phrase=Replace(phrase,"A","_")
phrase=Replace(phrase,"B","_")
phrase=Replace(phrase,"C","_")
phrase=Replace(phrase,"D","_")
phrase=Replace(phrase,"F","_")
phrase=Replace(phrase,"G","_")
phrase=Replace(phrase,"H","_")
phrase=Replace(phrase,"J","_")
phrase=Replace(phrase,"K","_")
phrase=Replace(phrase,"L","_")
phrase=Replace(phrase,"M","_")
phrase=Replace(phrase,"N","_")
phrase=Replace(phrase,"P","_")
phrase=Replace(phrase,"Q","_")
phrase=Replace(phrase,"R","_")
phrase=Replace(phrase,"S","_")
phrase=Replace(phrase,"T","_")
phrase=Replace(phrase,"V","_")
phrase=Replace(phrase,"W","_")
phrase=Replace(phrase,"X","_")
phrase=Replace(phrase,"Y","_")
phrase=Replace(phrase,"Z","_")
Do
result=InputBox "Player 2 enter your guess for" & phrase , "Guessing Game"
Loop until result==answer
msgbox "You got it!",vbokonly,"Guessing Game"
In VBScript, the 'equal' comparison operator is =. So change
Loop until result==answer
==>
Loop until result = answer
You're getting that error, because you used a function in an assignment without putting its parameter list in parentheses. Change this line:
result=InputBox "Player 2 enter your guess for" & phrase , "Guessing Game"
into this:
result=InputBox("Player 2 enter your guess for" & phrase , "Guessing Game")
This is one of VBScript's gotchas: depending on where/how you call a procedure or function, you must or mustn't put the parameter list in parentheses.
>>> String 3, "*" 'must not use parentheses here
>>> String(3, "*")
Cannot use parentheses when calling a Sub (0x414)
>>> Call String(3, "*") 'must use parentheses here
>>> Call String 3, "*"
Expected end of statement (0x401)
>>> v = String(3, "*") 'must use parentheses here either
>>> v = String 3, "*"
Expected end of statement (0x401)
To make matters worse, there are situations where parentheses can be used anyway, because they have a different meaning in that context:
>>> Hour(Now)
This actually works, because here the parentheses do not mean "parameter list", but "pass this argument by value". Take a look this article about the many interesting situations parentheses can create in VBScript.
The other mistake in your script, as Ekkehard.Horner already pointed out, is that you use == instead of = for comparing values.
As a side note: you seem to assume that the input will always consist of uppercase letters only, but you never enforce that anywhere. You may want to UCase your input or add a check to validate the input.
Seeing jmvp do such a good job optimizing your program made me want to do the same! :)
Here's my version. I added a constant to specify the application name (since it's used a few times) and also allow player 2 to quit playing by clicking Cancel (or entering nothing).
Const APP_TITLE = "Guessing Game"
strPhrase = InputBox("Player 1: Enter a phrase", APP_TITLE)
With New RegExp
.Pattern = "[^AEIOU]"
.Global = True
strDisplay = .Replace(strPhrase, "_")
End With
Do
strResult = InputBox("Player 2: Enter your guess for " & strDisplay, APP_TITLE)
Loop Until StrComp(strPhrase, strResult, vbBinaryCompare) = 0 Or Len(strResult) = 0
If Len(strResult) > 0 Then MsgBox "You got it!", vbOkOnly, APP_TITLE
Dim phrase As String = InputBox("Player 1: Enter a phrase", "Guessing Game")
Dim charCount As Integer = phrase.Length
Dim strMask As String = vbEmpty
For x = 1 To charCount
strMask &= "_"
Next
Dim guess As String = vbEmpty
Do
guess = InputBox("Player 2 enter your guess for " & strMask, "Guessing Game")
Loop Until phrase = guess
MsgBox("You got it!", vbOKOnly, "Guessing Game")

Resources