vbscript Eval a string to a Variable in a loop? - string

I am trying to use vbscript's Eval (or maybe I need Execute) to create some variables from the key names from an ini file. The ini file can have unlimited unknown key=val pairs. I need to create a variable based on the key name no matter what.
Ini File contents:
myPath=c:\test
myExe=myapp.exe
....
xxx=123
yyy=abc
My code that reads the ini and returns the key and values to an object
The code I am trying to get working is here:
For each pair in objINI
Eval("pair.key=pair.val")
Next
msgbox myPath
msgbox myExe
But both msgbox's are showing empty
And yes I am sure pair.key and pair.val have the correct values.
Thoughts on what I am missing or if this is even possible?

You need to Execute (an assign statement), not to Eval(uate a boolean expression):
>> n = "Name"
>> v = "Value"
>> WScript.Echo TypeName(Eval("n=v"))
>>
Boolean
>> Execute "n=v"
>> WScript.Echo n
>>
Value
>>
From the docs:
In VBScript, x = y can be interpreted two ways. The first is as an
assignment statement, where the value of y is assigned to x. The
second interpretation is as an expression that tests if x and y have
the same value. If they do, result is True; if they are not, result is
False. The Execute statement always uses the first interpretation,
whereas the Eval method always uses the second.
(This does not mean you should do such things; neither at home, nor at work)

You eval'd the literal code pair.key = pair.value.
That assigns to pair.key.
You want to assign to the value of pair.key – if pair.key is myPath, you want to eval myPath = pair.value.
You can do that by concatenating strings:
Execute(pair.name + " = pair.value")

If you want to read key/value pairs from an INI file you'd be better off storing them in a dictionary. I wrote a function for this some years ago. Basically looks like this:
Function ParseIni(filename)
Set ParseIni = Nothing
Set config = CreateObject("Scripting.Dictionary")
section = ""
Set file = CreateObject("Scripting.FileSystemObject").OpenTextFile(filename)
Do While Not file.AtEndOfStream
line = Trim(Replace(file.ReadLine, vbTab, " "))
If InStr(line, ";") > 0 Then line = Trim(Left(line, InStr(line, ";") - 1))
If line <> "" Then
If Left(line, 1) = "[" And Right(line, 1) = "]" Then
' line is a section name
section = Trim(Mid(line, 2, Len(line) - 2))
If section = "" Then _
WScript.Echo "Parse Error: section name is empty string."
If config.Exists(section) Then _
WScript.Echo "Parse Error: duplicate section name '" & name & "'."
config.Add section, CreateObject("Scripting.Dictionary")
ElseIf InStr(line, "=") > 0 Then
' line is a parameter line
If section = "" And Not config.Exists(section) Then _
config.Add section, CreateObject("Scripting.Dictionary")
param = Split(line, "=", 2)
param(0) = Trim(param(0))
param(1) = Trim(param(1))
If param(0) = "" Then _
WScript.Echo "Parse Error: invalid parameter name '" & param(0) & "'."
If param(1) = "" Then param(1) = True
config(section).Add param(0), param(1)
Else
' line is neither parameter nor section name, thus invalid
WScript.Echo "Parse Error: expected parameter definition in line '" _
& line & "'."
End If
End If
Loop
file.Close
Set ParseIni = config
End Function

Related

Lotusscript: Problem to convert an Array into a String

I have a problem when I try to convert my Array into a comma-separated String. In the example below, I retrieve all the files in my email and send them to my server to add them. My server sends me back an ID that is stored in the Array. Then I try to convert this Array into a String.
Dim filesId(1 To 100) As String
Dim tmpString As String
Dim count As Integer
count = 0
Set db = Session.Currentdatabase
Set CurrentDocColl = db.Unprocesseddocuments
Set doc = CurrentDocColl.Getfirstdocument
While Not doc Is Nothing
Set item = doc.GETFIRSTITEM("Body")
If doc.HasEmbedded Then
ForAll attachment In item.EmbeddedObjects
jsonBody="value={""filename"":"""+attachment.Name+"""}"
Set http=session.CreateHTTPRequest()
http.preferstrings = True
Call http.SetHeaderField("ContentType","application/json")
ret = http.Post(url, jsonBody)
If IsNumeric(ret) Then
count = count + 1
filesId(count) = ret
Else
MessageBox ret
End If
End ForAll
End If
Set doc=CurrentDocColl.Getnextdocument(doc)
Wend
ForAll itemValue In filesId
If CStr(itemValue ) <> "" Then
If tmpString = "" Then
tmpString = itemValue
Else
tmpString = tmpString & "," & itemValue
End If
End If
End ForAll
MessageBox tmpString
The problem is that the final String contains only the first value of the array and not the next values.
Example with this Array: [3567,3568,3569,3570]
Desired result String: 3567,3568,3569,3570
Result received: 3567
I don't understand where this problem comes from, especially since it also doesn't work with the Join() and Implode() functions.
EDIT
Indeed after having looked in the debugger, we can see that my data are present in the Array but in a particular format because the quotes of the strings do not close. What can I do to fix this?
Thank you very much for your help
Your http post result contains line break at its end. That is why the string looks so "strange" in debugger. This resuls in the following tmpString:
"3267
,3268
,3269
,3270"
Messagebox is not able to show all line breaks... so it only shows the string until the first line break.
You need to remove line breaks from your string before concatenating:
Dim badChars(2) as String
Dim goodChars(2) as String
badChars(0) = Chr$(0)
badChars(1) = Chr$(10)
badChars(2) = Chr$(13)
...
filesId(count) = Replace( ret, badChars, goodChars )
As I do not know WHICH line break / carriage return is there in your string I replace the three most common ones with blank in above code... Might be another unprintable character in there that you have to get rid of, then you need to examine Right( ret , 1) and check, what is in there and replace that.
I don't see a dim statement for tmpString. If there is no dim, then it is a variant. If it is a variant, then your assignment tmpString = item is dynamically defining tmpString as a numeric type. That would cause your assignment tmpString = tmpString & "," & item to fail. Since you appear to be expecting numeric data for ret, and you are assigning that into the filesId array, and item is a value from filesId, you need to be using CStr(item) in both of your assignments.

Manipulates String with VBA

I am trying to analyze a String with VBA code.
I read a String that looks like that :
myStringInput = "FRAG_INST = someValue,DR = otherValue, FRAG = anotherValue"
And in my code I would like to associate some variable according to the value read in the string, I would like to initialize my variables like that :
Dim dr, frag, fraginst As String
fraginst = someValue
dr = otherValue
frag = anotherValue
I have tried things like Trim/Split/InStr combination but I always ended up with wrong values.
I cannot just use "Mid" function because length of the values mays change from one execution to another...
To be clearer, I need to design function like this
fraginst = NiceFunction("FRAG_INST",myStringInput)
and it would return "someValue"
Is there an easy way to do what I want ?
Thanks
This solution is working fine. May be you can try this. I have not used any Regular expressions though.
Approach:
I first splitted the string by delimiter ,(comma). Traversed through each of the array elements and splitted each element by '='. Compared the value to the string present on the left side of '=' and returned value to the right of '=' after trimming. Mid function was needed.
myStringInput = "FRAG_INST = someValue,DR = otherValue, FRAG = anotherValue"
fraginst = NiceFunction("FRAG_INST",myStringInput)
MsgBox fraginst
Function NiceFunction(str1, str2)
tempArr1 = Split(str2,",")
For i=0 To UBound(tempArr1)
tempArr2 = Split(tempArr1(i),"=")
If StrComp(Trim(tempArr2(0)),str1,1)=0 Then
NiceFunction = Trim(tempArr2(1))
Exit For
End If
Next
End Function
Function NiceFunction( varName, inputString )
Dim match
With CreateObject("VBScript.RegExp")
.IgnoreCase = True
.Global = False
.Pattern = "(?:^|,)\s*" & varName & "\s*=\s*([^,]*)"
For Each match in .Execute( inputString )
NiceFunction = match.subMatches.Item(0)
Next
End With
End Function
You can use a RegExp object to extract the part of the string you need.
Instead of insisting on 'single' variables (DR, ...) which would need the dynamic creation of variables at run-time, you should use a dictionary:
Option Explicit
Function s2d(s)
If IsEmpty(gR) Then
Set gR = New RegExp
gR.Global = True
gR.Pattern = "(\w+)\s?=\s?(\w+)"
End If
Set s2d = CreateObject("Scripting.Dictionary")
Dim m
For Each m In gR.Execute(s)
If s2d.Exists(m.SubMatches(0)) Then
' Error: dup key
Else
s2d.Add m.SubMatches(0), m.SubMatches(1)
End If
Next
End Function
Function qq(s)
qq = """" & s & """"
End Function
Dim gR ' "static" in s2d()
Dim s : s = "FRAG_INST = someValue,DR = otherValue, FRAG = anotherValue"
If 0 < WScript.Arguments.Count Then s = WScript.Arguments(0)
Dim d : Set d = s2d(s)
Dim k
For Each k In d.Keys()
WScript.Echo qq(k), "=>", qq(d(k))
Next
If d.Exists("DR") Then WScript.Echo "DR:", d("DR") ' access 'single' var
Output:
cscript 44282497.vbs
"FRAG_INST" => "someValue"
"DR" => "otherValue"
"FRAG" => "anotherValue"
DR: otherValue
P.S.
I used a RegExp, because variable names must be 'words'/match "\w+" and your sample data values dito, whereas the separators look like a mess/use spaces creatively. See here fore how to use RegExps in VBA.

How to remove the last occurrence of a character within a string?

I'm currently writing a function that dynamically composes a sql-query to retreive a number of posts and I've run into a smaller problem.
Pseudocode:
if trim$(sSqlQuery) <> "" then
sSqlQuery = "foo foo ) foo"
end if
if 1 = 1 then
sSqlQuery = sSqlQuery & "bar bar bar"
end if
This function returns the correct sql-query most of the time, but due to some circumstances in the earlier functions before this one, the second if-clause will be triggered. Resulting in weird query-results.
What i need to do is to figure out how to remove the last occurrence of ")" within sSqlQuery before it appends the second set of query to the total query within the second if-clause.
In pseudo I think it'd look something like this:
if 1 = 1 then
call removeLastOccurringStringFromString(sSqlQuery, ")")
sSqlQuery = sSqlQuery & "bar bar bar"
end if
However, i find it really hard to get a grasp on the Right() Left() and Mid() functions.
What I have tried is this:
nLen = InStrRev(sSqlSokUrval, ")") ' To get the positional value of the last ")"
After that i'm completely lost. Since if I substring this with Mid() i'll only get the ")" and nothing else.
Any thoughts and/or hint's on how to go about solving this will be highly appreciated! Thanks!
'Searches subject and removes last (and *only* last) occurence of the findstring
Function RemoveLastOccurenceOf(subject, findstring)
Dim pos
'Find last occurence of findstring
pos = InstrRev(subject, findstring, -1, vbBinaryCompare) 'use vbTextCompare for case-INsensitive search
if pos>0 then 'Found it?
'Take left of subject UP UNTIL the point where it was found
'...Skip length of findstring
'...Add rest of string
RemoveLastOccurenceOf = Left(subject, pos - 1) & Mid(subject, pos + len(findstring))
else 'Nope
'Return entire subject
RemoveLastOccurenceOf = subject
end if
End Function

Syntax error, insert ";" to complete Statement. Multiple "if" in iReport expression if (cond) then expr1 else expr2 endif

I have to convert some reports from Crystal to Jasper and Groovy gives me headaches. I have an expression with multiple "if":
if (cond) then expr1 else expr2 endif.
In iReport this comes (cond) ? expr1 : expr2, but if i have another if condition and i place it under the first one i get erors. Could you give me some advice? Thank you!
Expression to convert:
if not isnull({ZEM0000_T.PRUEFMERKMAL}) then
text = {ZEM0000_T.PRUEFMERKMAL}
else
text = ""
end if
if not isnull ({ZEM0000_T.ANWEISUNG1}) then
text = text & Chr(13) & trim({ZEM0000_T.ANWEISUNG1})
end if
if not isnull ({ZEM0000_T.ANWEISUNG2}) then
text = text & Chr(13) & trim({ZEM0000_T.ANWEISUNG2})
end if
if not isnull ({ZEM0000_T.ANWEISUNG3}) then
text = text & Chr(13) & trim({ZEM0000_T.ANWEISUNG3})
end if
if not isnull ({#OT_NM_UT_2}) then
text = text & Chr(13) & {#OT_NM_UT_2}
end if
formula = text
Expressions in JasperReports are limited to a single expression (i.e. a single statement in Java/Groovy). They cannot be given a long script of code to execute. Your expression from Crystal Reports contains many statements, so converting the code to Groovy won't be enough, you will also have to refactor it to be one statement.
Luckily, as you are concatenating strings, you can just use the + operator to join each statement. So the code from your comment
text = ($F{PRUEFMERKMAL} != null) ? $F{PRUEFMERKMAL} : ""
text = ($F{ANWEISUNG1} != null) ? text + chr(13) + trim($F{ANWEISUNG1}) : text
would need to look something like this:
(($F{PRUEFMERKMAL} != null) ? $F{PRUEFMERKMAL} : "") +
(($F{ANWEISUNG1} != null) ? text + chr(13) + trim($F{ANWEISUNG1}) : "")

I need to find the number of 3, 4, 5 and 6 letter words in a string using VBScript

Here's the question I have to answer for my assignment:
Count the number of words in the string "tx_val" that have 3,4,5 or 6 chatacters. Show these four counts on a single line seperated by commas in the span block id="ans12".
Here's what I've come up with, the output is incorrect and I'm not sure why. I'll post below. Thought I'd give you all a update of where I was at with it.
threematch = 0
fourmatch = 0
fivematch = 0
sixmatch = 0
totalmatch = ""
cntArr = Array()
cntArr = Split(tx_val," ")
i=0
For i=0 To Ubound(cntArr) Step 1
If len(cstr(cntArr(i))) = 3 Then
threecount = threecount + 1
ElseIf len(cstr(cntArr(i))) = 4 Then
fourcount = fourcount + 1
ElseIf len(cstr(cntArr(i))) = 5 Then
fivecount = fivecount + 1
ElseIf len(cstr(cntArr(i))) = 6 Then
sixcount = sixcount + 1
End If
i=i+1
Next
totalmatch = (threecount & ", " & fourcount & ", " & fivecount & ", " & sixcount & ".")
document.getElementById("ans12").innerHTML = totalmatch
First, and this is what is causing the wrong behaviour, you are explicitly incrementing your counter i, even though the For-Next loop already does that for you. The result is that for each pass through the loop, i actually gets incremented by 2.
Remove the i=i+1 line and your script will work as intended.
Second, your variable names are inconsistent, being initialised as e.g. threematch and later used as threecount. You should always declare your variables explicitly (Dim statements) and write Option Explicit at the top of your code to catch such obvious mistakes at compile time. By pure chance this mistake does not actually cause any errors in your particular case.
If you are comfortable using regular expressions in JavaScript, why not use them in VBScript? They both use the same ECMA-262 so patterns are identical between the two languages. VBScript's RegExp object can do the same thing as your example.
Set re = New RegExp
re.IgnoreCase = True ' equivalent to /i modifier
re.Global = True ' equivalent to /g modifier
re.Pattern = "\b\w{3}\b" ' regex pattern without delimiters or modifiers
Set colMatches = re.Execute(someStringOfWords)
intCount = colMatches.Count
To learn more about Regular Expressions in VBScript, stop by the MSDN and read Microsoft Beefs Up VBScript with Regular Expressions.
Break the problem up:
1) Extract all words (break on whitespace) into a list
2) Iterate over the list, checking which words have the specified number of characters, increment a counter each time a matching word length is seen.
3) Write out the total counts

Resources