best way to decode a string

Just starting out? Need help? Post your questions and find answers here.
ludoke
Enthusiast
Enthusiast
Posts: 155
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

best way to decode a string

Post 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?
User avatar
STARGÅTE
Addict
Addict
Posts: 2226
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: best way to decode a string

Post 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.
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 moreTypeface - Sprite-based font include/module
User avatar
Kiffi
Addict
Addict
Posts: 1485
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Re: best way to decode a string

Post 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
Hygge
User avatar
NicTheQuick
Addict
Addict
Posts: 1504
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: best way to decode a string

Post 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. ;-)
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.
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: best way to decode a string

Post 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.
User avatar
mk-soft
Always Here
Always Here
Posts: 6204
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: best way to decode a string

Post 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)
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
ebs
Enthusiast
Enthusiast
Posts: 557
Joined: Fri Apr 25, 2003 11:08 pm

Re: best way to decode a string

Post 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.
User avatar
NicTheQuick
Addict
Addict
Posts: 1504
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: best way to decode a string

Post 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²).
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.
User avatar
JHPJHP
Addict
Addict
Posts: 2251
Joined: Sat Oct 09, 2010 3:47 am

Re: best way to decode a string

Post 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

If you're not investing in yourself, you're falling behind.

My PureBasic StuffFREE STUFF, Scripts & Programs.
My PureBasic Forum ➤ Questions, Requests & Comments.
User avatar
mk-soft
Always Here
Always Here
Posts: 6204
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: best way to decode a string

Post 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
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
infratec
Always Here
Always Here
Posts: 7577
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: best way to decode a string

Post 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
Last edited by infratec on Fri Nov 20, 2020 9:07 pm, edited 1 time in total.
ludoke
Enthusiast
Enthusiast
Posts: 155
Joined: Fri Jul 08, 2016 5:35 pm
Location: Essen (Belgium)

Re: best way to decode a string

Post by ludoke »

thanks for all the thinking.
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.
infratec
Always Here
Always Here
Posts: 7577
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: best way to decode a string

Post 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.
infratec
Always Here
Always Here
Posts: 7577
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: best way to decode a string

Post 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

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
Post Reply