I would like to get the values from points that i have connected from control system, using basic script engine. The problem is there are many points, so i want to build a somewhat loop like below :
For i As New Points = 0 To 70
Dim Para + i AS OracleParameter
PointGet("SST(i)_H_MTR")
Next i
instead of doing this :
dim SST01 as New Points
dim SST02 as New Points
dim SST03 as New Points
pointget(SST01_H_MTR)
pointget(SST02_H_MTR)
pointget(SST03_H_MTR)
the problem is, the points can not be defined as an array. Would somebody have found same problem with me ? any open idea is very much appreciated. Thank you
I know this is old but here you go.
Dim ptMTR as New Point
Dim pointName as String
Dim pointType as String
Dim MTR_Value as Integer
Dim i as Integer
pointType = "_H_MTR"
For i = 0 To 70
if i < 10 then
pointName = "SST0"
else
pointName = "SST"
end if
ptMTR.Id = pointName + cstr(i) + pointType
MTR_Value = ptMTR.GetValue
'Write code to use the point value here
'Go get the next point value
next i
...
Related
I want to extract characters from a string. However, the string doesn't have the same length every time.
Basically I get data from a database and I want extract the value I need in it. But I'm stuck on step where I have to extract the right value.
So first I get data like that :
infoDataset2 = accessRequet_odbc("select st_astext(st_snaptogrid(geom, 0.01)) from netgeo_point_tech", myConnection)
The result is something like : POINT(921021.98 6671778.45). What I need are the 2 figures, but their length is not fixed. I just want to remove POINT( and ).
Then I work on each line of the DataSet I get to cast each lines into a string with only the value needed.
For i = 0 To infoDataset2.Tables(0).Rows.Count - 1
geomPt = infoDataset2.Tables(0).Rows(i).ItemArray(0).Substring(geomPt = infoDataset2.Tables(0).Rows(i).ItemArray(0).Substring(1 + infoDataset2.Tables(0).Rows(i).ItemArray(0).LastIndexOf("(")))
Console.WriteLine(geomPt)
Next
This was my last try, where I was able to remove POINT( but I'm struggling with the length to cut ).
I want to learn from this, so, if possible, explain to me what I'm doing wrong here, or if my approach is lacking insight.
It will be horrible to debug that one long line. It makes no difference to the computer if you split it up into easy-readable parts. Here's some code to get the x- and y-coordinates from a string formatted as shown in the question:
Dim s = "POINT(921021.98 6671778.45)"
Dim b1 = s.IndexOf("("c) + 1
Dim b2 = s.IndexOf(")"c, b1) - 1
Dim parts = s.Substring(b1, b2 - b1 + 1).Split({" "c})
Dim x As Decimal = Decimal.Parse(parts(0))
Dim y As Decimal = Decimal.Parse(parts(1))
Another way of parsing the string is to use a regular expression, which can be more flexible. In this example, I used named capture groups to make it easy to see which parts are for the x and y:
Dim s = "POINT(921021.98 6671778.45)"
Dim x As Decimal
Dim y As Decimal
Dim re = New Regex("\((?<x>[0-9-.]+) (?<y>[0-9-.]+)\)")
Dim m = re.Match(s)
If m.Success Then
x = Decimal.Parse(m.Groups("x").Value)
y = Decimal.Parse(m.Groups("y").Value)
Else
' Could not parse point. Do something about it if required.
End If
Andrew Morton has given a nice answer, i upvoted that one, if you need an even easier way and that was still complicated use this
Dim s = "POINT(921021.98 6671778.45)"
Dim part1 As String = s.Remove(0, 6)
Dim part2 As String = part1.Substring(0, part1.Length - 1)
Dim split() As String = part2.Split(" ")
Dim x = split(0)
Dim y = split(1)
Here is an even probably easier to understand solution:
Dim s as String = "POINT(921021.98 6671778.45)"
Dim coordinate() as String = s.Replace("POINT(", "").Replace(")", "").Split(" ")
Enjoy!
Sorry for the terrible wording on my last question, I was half asleep and it was midnight. This time I'll try to be more clear.
I'm currently writing some code for a mini barcode scanner and stock manager program. I've got the input and everything sorted out, but there is a problem with my arrays.
I'm currently trying to extract the contents of the stock file and sort them out into product tables.
This is my current code for getting the data:
Using fs As StreamReader = New StreamReader("The File Path (Is private)")
Dim line As String = "ERROR"
line = fs.ReadLine()
While line <> Nothing
Dim pos As Integer = 0
Dim split(3) As String
pos = products.Length
split = line.Split("|")
productCodes(productCodes.Length) = split(0)
products(products.Length, 0) = split(1)
products(products.Length, 1) = split(2)
products(products.Length, 2) = split(3)
line = fs.ReadLine()
End While
End Using
I have made sure that the file path does, in fact, go to the file. I have looked through debug to find that all the data is going through into my "split" table. The error throws as soon as I start trying to transfer the data.
This is where I declare the two tables being used:
Dim productCodes() As String = {}
Dim products(,) As Object = {}
Can somebody please explain why this is happening?
Thanks in advance
~Hydro
By declaring the arrays like you did:
Dim productCodes() As String = {}
Dim products(,) As Object = {}
You are assigning size 0 to all your arrays, so during your loop, it will eventually try to access a position that haven't been previously declared to the compiler. It is the same as declaring an array of size 10 Dim MyArray(10) and try to access the position 11 MyArray(11) = something.
You should either declare it with a proper size, or redim it during execution time:
Dim productCodes(10) As String
or
Dim productCodes() As String
Dim Products(,) As String
Dim Position as integer = 0
'code here
While line <> Nothing
Redim Preserve productCodes(Position)
Redim Preserve products(2,Position)
Dim split(3) As String
pos = products.Length
split = line.Split("|")
productCodes(Position) = split(0)
products(0,Position) = split(1)
products(1,Position) = split(2)
products(2,Position) = split(3)
line = fs.ReadLine()
Position+=1
End While
I'm learning for loop and I cannot get this problem fixed.
The problems are in the following codes.
dim rt as integer = 2
dim i As Integer = 0
dim currentpg as string = "http://homepg.com/"
For i = 0 To rt
currentpg = currentpg & "?pg=" & i
messagebox.show(currentpg)
next
'I hoped to get the following results
http://homepg.com/?pg=0
http://homepg.com/?pg=1
http://homepg.com/?pg=2
'but instead I'm getting this
http://homepg.com/?pg=0
http://homepg.com/?pg=0?pg=0
http://homepg.com/?pg=0?pg=0?pg=0
Please help me
Thank you.
You probably need something like this:
Dim basepg as string = "http://homepg.com/"
For i = 0 To rt
Dim currentpg As String = basepg & "?pg=" & i
messagebox.show(currentpg)
Next
Although a proper approach would be to accumulate results into a List(Of String), and then display in a messagebox once (or a textbox/file, if too many results). You don't want to bug user for every URL (what if there are 100 of them?). They would get tired of clicking OK.
First of all, you went wrong while copying the output of the buggy code. Here is the real one.
http://homepg.com/?pg=0
http://homepg.com/?pg=0?pg=1
http://homepg.com/?pg=0?pg=1?pg=2
It does not work because currentpg should be a constant but it is changed on each iteration.
Do not set, just get.
MessageBox.Show(currentpg & "?pg=" & i)
Or you can use another variable to make it more readable.
Dim newpg As String = currentpg & "?pg=" & i
MessageBox.Show(newpg)
Also, your code is inefficient. I suggest you to change it like this.
Dim iterations As Integer = 2
Dim prefix As String = "http://homepg.com/?pg="
For index As Integer = 0 To iterations
MessageBox.Show(prefix & index)
Next
I am looking to implement a VBA trie-building algorithm that is able to process a substantial English lexicon (~50,000 words) in a relatively short amount of time (less than 15-20 seconds). Since I am a C++ programmer by practice (and this is my first time doing any substantial VBA work), I built a quick proof-of-concept program that was able to complete the task on my computer in about half a second. When it came time to test the VBA port however, it took almost two minutes to do the same -- an unacceptably long amount of time for my purposes. The VBA code is below:
Node Class Module:
Public letter As String
Public next_nodes As New Collection
Public is_word As Boolean
Main Module:
Dim tree As Node
Sub build_trie()
Set tree = New Node
Dim file, a, b, c As Integer
Dim current As Node
Dim wordlist As Collection
Set wordlist = New Collection
file = FreeFile
Open "C:\corncob_caps.txt" For Input As file
Do While Not EOF(file)
Dim line As String
Line Input #file, line
wordlist.add line
Loop
For a = 1 To wordlist.Count
Set current = tree
For b = 1 To Len(wordlist.Item(a))
Dim match As Boolean
match = False
Dim char As String
char = Mid(wordlist.Item(a), b, 1)
For c = 1 To current.next_nodes.Count
If char = current.next_nodes.Item(c).letter Then
Set current = current.next_nodes.Item(c)
match = True
Exit For
End If
Next c
If Not match Then
Dim new_node As Node
Set new_node = New Node
new_node.letter = char
current.next_nodes.add new_node
Set current = new_node
End If
Next b
current.is_word = True
Next a
End Sub
My question then is simply, can this algorithm be sped up? I saw from some sources that VBA Collections are not as efficient as Dictionarys and so I attempted a Dictionary-based implementation instead but it took an equal amount of time to complete with even worse memory usage (500+ MB of RAM used by Excel on my computer). As I say I am extremely new to VBA so my knowledge of both its syntax as well as its overall features/limitations is very limited -- which is why I don't believe that this algorithm is as efficient as it could possibly be; any tips/suggestions would be greatly appreciated.
Thanks in advance
NB: The lexicon file referred to by the code, "corncob_caps.txt", is available here (download the "all CAPS" file)
There are a number of small issues and a few larger opportunities here. You did say this is your first vba work, so forgive me if I'm telling you things you already know
Small things first:
Dim file, a, b, c As Integer declares file, a and b as variants. Integer is 16 bit sign, so there may be risk of overflows, use Long instead.
DIM'ing inside loops is counter-productive: unlike C++ they are not loop scoped.
The real opportunity is:
Use For Each where you can to iterate collections: its faster than indexing.
On my hardware your original code ran in about 160s. This code in about 2.5s (both plus time to load word file into the collection, about 4s)
Sub build_trie()
Dim t1 As Long
Dim wd As Variant
Dim nd As Node
Set tree = New Node
' Dim file, a, b, c As Integer : declares file, a, b as variant
Dim file As Integer, a As Long, b As Long, c As Long ' Integer is 16 bit signed
Dim current As Node
Dim wordlist As Collection
Set wordlist = New Collection
file = FreeFile
Open "C:\corncob_caps.txt" For Input As file
' no point in doing inside loop, they are not scoped to the loop
Dim line As String
Dim match As Boolean
Dim char As String
Dim new_node As Node
Do While Not EOF(file)
'Dim line As String
Line Input #file, line
wordlist.Add line
Loop
t1 = GetTickCount
For Each wd In wordlist ' for each is faster
'For a = 1 To wordlist.Count
Set current = tree
For b = 1 To Len(wd)
'Dim match As Boolean
match = False
'Dim char As String
char = Mid$(wd, b, 1)
For Each nd In current.next_nodes
'For c = 1 To current.next_nodes.Count
If char = nd.letter Then
'If char = current.next_nodes.Item(c).letter Then
Set current = nd
'Set current = current.next_nodes.Item(c)
match = True
Exit For
End If
Next nd
If Not match Then
'Dim new_node As Node
Set new_node = New Node
new_node.letter = char
current.next_nodes.Add new_node
Set current = new_node
End If
Next b
current.is_word = True
Next wd
Debug.Print "Time = " & GetTickCount - t1 & " ms"
End Sub
EDIT:
loading the word list into a dynamic array will reduce load time to sub second. Be aware that Redim Preserve is expensive, so do it in chunks
Dim i As Long, sz As Long
sz = 10000
Dim wordlist() As String
ReDim wordlist(0 To sz)
file = FreeFile
Open "C:\corncob_caps.txt" For Input As file
i = 0
Do While Not EOF(file)
'Dim line As String
Line Input #file, line
wordlist(i) = line
i = i + 1
If i > sz Then
sz = sz + 10000
ReDim Preserve wordlist(0 To sz)
End If
'wordlist.Add line
Loop
ReDim Preserve wordlist(0 To i - 1)
then loop through it like
For i = 0 To UBound(wordlist)
wd = wordlist(i)
I'm out of practice with VBA, but IIRC, iterating the Collection using For Each should be a bit faster than going numerically:
Dim i As Variant
For Each i In current.next_nodes
If i.letter = char Then
Set current = i
match = True
Exit For
End If
Next node
You're also not using the full capabilities of Collection. It's a Key-Value map, not just a resizeable array. You might get better performance if you use the letter as a key, though looking up a key that isn't present throws an error, so you have to use an ugly error workaround to check for each node. The inside of the b loop would look like:
Dim char As String
char = Mid(wordlist.Item(a), b, 1)
Dim node As Node
On Error Resume Next
Set node = Nothing
Set node = current.next_nodes.Item(char)
On Error Goto 0
If node Is Nothing Then
Set node = New Node
current.next_nodes.add node, char
Endif
Set current = node
You won't need the letter variable on class Node that way.
I didn't test this. I hope it's all right...
Edit: Fixed the For Each loop.
Another thing you can do which will possibly be slower but will use less memory is use an array instead of a collection, and resize with each added element. Arrays can't be public on classes, so you have to add methods to the class to deal with it:
Public letter As String
Private next_nodes() As Node
Public is_word As Boolean
Public Sub addNode(new_node As Node)
Dim current_size As Integer
On Error Resume Next
current_size = UBound(next_nodes) 'ubound throws an error if the array is not yet allocated
On Error GoTo 0
ReDim next_nodes(0 To current_size) As Node
Set next_nodes(current_size) = new_node
End Sub
Public Function getNode(letter As String) As Node
Dim n As Variant
On Error Resume Next
For Each n In next_nodes
If n.letter = letter Then
Set getNode = n
Exit Function
End If
Next
End Function
Edit: And a final optimization strategy, get the Integer char value with the Asc function and store that instead of a String.
You really need to profile it, but if you think Collections are slow maybe you can try using dynamic arrays?
Currently I'm trying to fill a 3x3 square with random x's and o's to make a tic tac toe
game. Unfortunately the game doesn't seem to output all the x's and o's. Logically, from what I can see, it should be able to but it's not. Any help would be appreciated.
Shared Sub twodimension()
Dim tic(2, 2) As String
Dim min As Integer
Dim x As String
Dim random As New Random()
Dim i As Integer
Dim x1 As Integer
Dim bound0 As Integer = tic.GetUpperBound(0)
Dim bound1 As Integer = tic.GetLowerBound(1)
For i = 0 To bound0
For x1 = 0 To bound1
min = random.Next(2)
If min = 0 Then
x = "x"
Console.WriteLine("{0}", x)
Else
x = "o"
Console.WriteLine("{0}", x)
End If
Console.Write(" "c)
Next
Console.WriteLine()
Next
End Sub
So presumably you've got this declaration somewhere, right?
Public Shared Tic(2, 2) As String
In your code you've got GetLowerBound which will (almost) always returns zero and instead you should have GetUpperBound().
Dim bound0 As Integer = tic.GetUpperBound(0)
Dim bound1 As Integer = Tic.GetUpperBound(1)
EDIT (in response to comment)
GetUpperBound(int) returns the highest number that you can use for the dimension that you specify.
So for the following array:
Dim MyArray(4, 6, 8) As Integer
Trace.WriteLine(MyArray.GetUpperBound(0)) ''//Returns 4
Trace.WriteLine(MyArray.GetUpperBound(1)) ''//Returns 6
Trace.WriteLine(MyArray.GetUpperBound(2)) ''//Returns 8
GetLowerBound(int) returns the lowest number that you can use for the dimension that you specify. In almost every case this is zero but in older versions of VB (and using some COM interop) you can create arrays that don't "start" at zero and instead start at whatever you wanted. So in old VB you could actually say Dim Bob(1 To 4) As Integer and GetLowerBound(0) would return 1 instead of 0. For the most part there is no reason to even be aware that GetLowerBound exists even.