best way to decode a string
best way to decode a string
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?
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
You can use a character array like that:
*Characters\c[n] is the character code, its even faster than comparing the character string.
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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Lizard - Script language for symbolic calculations and more ― Typeface - Sprite-based font include/module
Re: best way to decode a string
Define "decode a string"ludoke wrote:what is the fastest way to decode a string line$ eg. "N100 G01 X1.25 Y1.4 Z2.0 F200"
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
Hygge
- NicTheQuick
- Addict
- Posts: 1504
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: best way to decode a string
@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.
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.

The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: best way to decode a string
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:Kiffi wrote:Define "decode a string"ludoke wrote:what is the fastest way to decode a string line$ eg. "N100 G01 X1.25 Y1.4 Z2.0 F200"
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
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
Needed some relaxation from the current project ...
Update 3
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)
Last edited by mk-soft on Fri Nov 20, 2020 8:57 pm, edited 6 times in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Re: best way to decode a string
My standard method of using StringField() is this:
It should be somewhat faster than using CountString(), since it only has to iterate over the string once, not twice.
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
- NicTheQuick
- Addict
- Posts: 1504
- Joined: Sun Jun 22, 2003 7:43 pm
- Location: Germany, Saarbrücken
- Contact:
Re: best way to decode a string
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²).ebs wrote:My standard method of using StringField() is this:
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
Re: best way to decode a string
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
If you're not investing in yourself, you're falling behind.
My PureBasic Stuff ➤ FREE STUFF, Scripts & Programs.
My PureBasic Forum ➤ Questions, Requests & Comments.
Re: best way to decode a string
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
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
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Re: best way to decode a string
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.
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
Last edited by infratec on Fri Nov 20, 2020 9:07 pm, edited 1 time in total.
Re: best way to decode a string
thanks for all the thinking.
I am going to try to understand all the suggested solutions.
I am going to try to understand all the suggested solutions.
Last edited by ludoke on Sat Nov 21, 2020 9:04 pm, edited 1 time in total.
Re: best way to decode a string
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.
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
Optimized version:
It removes spaces and CR LF ...
You can now read the complete file in one string and split it into single commands.
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
ReadString(0, #PB_File_IgnoreEOL)
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