Page 1 of 1
best way to decode a string
Posted: Fri Nov 20, 2020 10:31 am
by ludoke
what is the fastest way to decode a string line$ eg. "N100 G01 X1.25 Y1.4 Z2.0 F200"
or "N200 G0 x15.0 y20 I-10 j -20".
i read the string character by character
n=stringlength
for a=0 to n
character= Mid (line$, a, 1)
But is there a better and faster way?
Re: best way to decode a string
Posted: Fri Nov 20, 2020 11:09 am
by STARGÅTE
You can use a character array like that:
Code: Select all
Structure CharacterArray
c.c[0]
EndStructure
Define line$ = "N100 G01 X1.25 Y1.4 Z2.0 F200"
Define *Characters.CharacterArray = @line$
Define n
While *Characters\c[n]
Debug Str(*Characters\c[n]) + "("+Chr(*Characters\c[n])+")"
n + 1
Wend
*Characters\c[n] is the character code, its even faster than comparing the character string.
Re: best way to decode a string
Posted: Fri Nov 20, 2020 11:50 am
by Kiffi
ludoke wrote:what is the fastest way to decode a string line$ eg. "N100 G01 X1.25 Y1.4 Z2.0 F200"
Define "decode a string"
When I look at your string, I assume that you want to split it into the individual informations "N100", "G01", "X1.25", etc.
In this case you could use
StringField():
Code: Select all
myString.s = "N100 G01 X1.25 Y1.4 Z2.0 F200"
CountFields = CountString(myString, " ")
For Counter = 1 To CountFields + 1
Debug StringField(myString, Counter, " ")
Next
Re: best way to decode a string
Posted: Fri Nov 20, 2020 12:40 pm
by NicTheQuick
@Kiffi:
Imo there should be a function which directly converts a string with delimiters to a dynamic array.
Why? CountString() iterates over the whole string to count all the delimiters. Then in your for loop every time you call StringField() it has to iterate over the string until it found the n-th delimiter. In the end you have a runtime of roughly O(n + n/k + 2·n/k + … + (k-1)·n/k) = O(n²)
Therefore I think STARGÅTEs idea is better in this case. Use `While` to iterate through the string once, recognize the character after each space, parse the number after it and store it somewhere.
I am assuming you want to parse a lot of G-Code here. Because such a file can consists 100 thousands or even millions of lines O(n²) would not be good here.

Re: best way to decode a string
Posted: Fri Nov 20, 2020 2:27 pm
by Tenaja
Kiffi wrote:ludoke wrote:what is the fastest way to decode a string line$ eg. "N100 G01 X1.25 Y1.4 Z2.0 F200"
Define "decode a string"
When I look at your string, I assume that you want to split it into the individual informations "N100", "G01", "X1.25", etc.
In this case you could use
StringField():
Code: Select all
myString.s = "N100 G01 X1.25 Y1.4 Z2.0 F200"
CountFields = CountString(myString, " ")
For Counter = 1 To CountFields + 1
Debug StringField(myString, Counter, " ")
Next
From the StringField, it would be trivial to convert to a Map, just remember to reset it between commands. So a hardcoded version of the above string would start like this:
GcodeMap("N") = 100
GcodeMap("G") = 1
The tricky part will be modal commands where eg the g01 is missing, so maybe instead of resetting the map, push it to a buffer, or store the current modal command separately.
Re: best way to decode a string
Posted: Fri Nov 20, 2020 3:23 pm
by mk-soft
Needed some relaxation from the current project ...
Update 3
Code: Select all
; Parser
Structure udtData
N.i
G.i
X.f
Y.f
Z.f
F.i
EndStructure
Procedure ParseData(String.s, *Data.udtData)
Protected *mem.character, *pValue, Param.i
*mem = @String
If *mem = 0
ProcedureReturn 0
EndIf
Repeat
Select *mem\c
Case 'A' To 'Z', 'a' To 'z'
Param = *mem\c
Case '0' To '9', '.', '+', '-'
If Not *pValue
*pValue = *mem
EndIf
Default
If *pValue
If Param
Select Param
Case 'N','n' : *Data\N = Val(PeekS(*pValue, *mem - *pValue))
Case 'G','g' : *Data\G = Val(PeekS(*pValue, *mem - *pValue))
Case 'X','x' : *Data\X = ValF(PeekS(*pValue, *mem - *pValue))
Case 'Y','y' : *Data\Y = ValF(PeekS(*pValue, *mem - *pValue))
Case 'Z','z' : *Data\Z = ValF(PeekS(*pValue, *mem - *pValue))
Case 'F','f' : *Data\F = Val(PeekS(*pValue, *mem - *pValue))
EndSelect
EndIf
Param = 0
*pValue = 0
EndIf
EndSelect
If *mem\c = 0
Break
EndIf
*mem + SizeOf(Character)
ForEver
ProcedureReturn 1
EndProcedure
Procedure ShowData(*Data.udtData)
With *Data
Debug "N = " + \N
Debug "G = " + \G
Debug "X = " + \X
Debug "Y = " + \Y
Debug "Z = " + \Z
Debug "F = " + \F
Debug "****"
EndWith
EndProcedure
MyData.udtData
myString.s = "N100 G01 X1.25 Y1.4 Z2.0 F200"
ParseData(myString, MyData)
ShowData(MyData)
myString.s = "N300 A999 G011 x-5.5 y-5.4 Z6.06 F400"
ParseData(myString, MyData)
ShowData(MyData)
Re: best way to decode a string
Posted: Fri Nov 20, 2020 3:28 pm
by ebs
My standard method of using StringField() is this:
Code: Select all
myString.s = "N100 G01 X1.25 Y1.4 Z2.0 F200"
index.l = 1
Repeat
Field.s = StringField(myString, index, " ")
If Field
Debug Field
index + 1
Else
Break
Endif
Forever
It should be somewhat faster than using CountString(), since it only has to iterate over the string once, not twice.
Re: best way to decode a string
Posted: Fri Nov 20, 2020 4:54 pm
by NicTheQuick
ebs wrote:My standard method of using StringField() is this:
But this method only works if there are no empty columns allowed. In this case it works but still has a complexity of O(n²).
Re: best way to decode a string
Posted: Fri Nov 20, 2020 5:02 pm
by JHPJHP
Since we're just seeing what sticks, here are a couple Regular Expressions; modify Pattern to needs.
Code: Select all
Pattern$ = "[a-zA-Z0-9.]+\s*"
String$ = "N100 G01 X1.25 Y1.4 Z2.0 F200"
If CreateRegularExpression(0, Pattern$)
If ExamineRegularExpression(0, String$)
While NextRegularExpressionMatch(0)
Debug RegularExpressionMatchString(0)
Wend
EndIf
FreeRegularExpression(0)
EndIf
Debug "-----------------------------------------"
If CreateRegularExpression(0, Pattern$)
Dim Result$(0)
nResult = ExtractRegularExpression(0, String$, Result$())
For rtnCount = 0 To nResult - 1
Debug Result$(rtnCount)
Next
FreeRegularExpression(0)
EndIf
Re: best way to decode a string
Posted: Fri Nov 20, 2020 7:15 pm
by mk-soft
It's three years old ...
Link:
SplitString
But if you (probably) have to process the values as well, you can also return them as data structure.
viewtopic.php?f=13&t=76303&p=562071#p562058
Re: best way to decode a string
Posted: Fri Nov 20, 2020 7:37 pm
by infratec
StringField() needs to much time.
Same for Regex additional it includes a large library.
So I prefer the pointer variant.
With my version you can also read in the complete file in one string and parse it.
Code: Select all
EnableExplicit
Procedure.i ParseGCode(*Line, List GCodeList.s())
Protected *Char.Character, Char.i, *GCode
*Char = *Line
While *Char\c
Char = *Char\c
If (Char >= 'a' And Char <='z') Or (Char >= 'A' And Char <= 'Z')
If *GCode
AddElement(GCodeList())
GCodeList() = PeekS(*GCode, (*Char - *GCode) >> 1)
EndIf
*GCode = *Char
EndIf
*Char + 2
Wend
If *GCode < *Char
AddElement(GCodeList())
GCodeList() = PeekS(*GCode, (*Char - *GCode) >> 1)
EndIf
ProcedureReturn ListSize(GCodeList())
EndProcedure
Define Line1$, Line2$
NewList GCodeList.s()
Line1$ = "N100 G01 X1.25 Y1.4 Z2.0 F200"
Line2$ = "N200 G0 x15.0 y20 I-10 j -20"
If ParseGCode(@Line1$, GCodeList())
ForEach GCodeList()
Debug GCodeList()
Next
EndIf
Debug "----"
ClearList(GCodeList()) ; or comment it out if a complete list is wanted.
If ParseGCode(@Line2$, GCodeList())
ForEach GCodeList()
Debug GCodeList()
Next
EndIf
Re: best way to decode a string
Posted: Fri Nov 20, 2020 8:03 pm
by ludoke
thanks for all the thinking.
I am going to try to understand all the suggested solutions.
Re: best way to decode a string
Posted: Fri Nov 20, 2020 9:16 pm
by infratec
With my procedure you have nothing todo with pointers.
The procedure deals with them.
You only need the address of the string for calling the procedure.
In this case it has many advantages:
1. The string is not copied into the procedure, it stays at his place. (no addtional memory needed)
2. There are no repeating loops like with StringField() or Mid()
In general:
1. Avoid globals
Personally I don't use globals, it is to dangerous in a large program.
You can not know where and when the variable is modified.
At least if you are using threads.
2. A variable if global or not is not an address.
A variable is always a 'place' for a value.
You need always the address operator (@) to get the address of these place where the value is stored.
Re: best way to decode a string
Posted: Fri Nov 20, 2020 9:28 pm
by infratec
Optimized version:
It removes spaces and CR LF ...
You can now read the complete file in one string and split it into single commands.
Code: Select all
EnableExplicit
Procedure.i ParseGCode(*Line, List GCodeList.s())
Protected *Char.Character, Char.i, Ready.i
*Char = *Line
While *Char\c
Char = *Char\c
If (Char >= 'a' And Char <='z') Or (Char >= 'A' And Char <= 'Z')
AddElement(GCodeList())
Ready = #True
EndIf
If Ready And Char > ' '
GCodeList() + Chr(Char)
EndIf
*Char + 2
Wend
ProcedureReturn ListSize(GCodeList())
EndProcedure
Define Line1$, Line2$, Line3$
NewList GCodeList.s()
Line1$ = "N100 G01 X1.25 Y1.4 Z2.0 F200"
Line2$ = "N200 G0 x15.0 y20 I-10 j -20"
Line3$ = Line1$ + #CRLF$ + Line2$
If ParseGCode(@Line1$, GCodeList())
ForEach GCodeList()
Debug GCodeList()
Next
EndIf
Debug "----"
ClearList(GCodeList()) ; or not if a complete list is wanted.
If ParseGCode(@Line2$, GCodeList())
ForEach GCodeList()
Debug GCodeList()
Next
EndIf
Debug "----"
ClearList(GCodeList())
If ParseGCode(@Line3$, GCodeList())
ForEach GCodeList()
Debug GCodeList()
Next
EndIf